Saving score with NSUserDefaults: UInt8 not convertible to Int8 - swift

I'm creating a simple game. if the player loses i want his current score to be compared with the best score saved as persistent storage as NSUserDefault. i keep on getting "UInt8 is not convertible to Int8" this is the code
scoreForComparingWithBestScore = UInt8(score)
if ( scoreForComparingWithBestScore > NSUserDefaults.standardUserDefaults().objectForKey("BestScore")){
NSUserDefaults.standardUserDefaults().objectForKey("BestScore") = scoreForComparingWithBestScore
}
even if i typed
scoreForComparingWithBestScore = Int8(score)
the error will be switched.
Thanks in advance !

NSUserDefaults.standardUserDefaults().objectForKey("BestScore”) returns an AnyObject?. To compare this with a UInt8 (or any other Swift numeric value type – you might want to reconsider UInt8 and use Int instead unless there’s a very good reason to stick with that type), you need to both unwrap the optional and also convert it to a number:
if let best = NSUserDefaults.standardUserDefaults().objectForKey("BestScore")?.integerValue {
// best will be an Int here, you can compare it to scoreForComparingWithBestScore
}
else {
// either there was no BestScore value, or there was but it wasn't an integer
}
Alternatively, you could use the ?? operator to default the best score to zero if not present (or not an integer):
let best = NSUserDefaults
.standardUserDefaults()
.objectForKey("BestScore")?
.integerValue ?? 0

NSSUserDefaults has convenience methods to store different types of objects.
In your case, rather than storing objects directly and then trying to convert them to the correct type, you could just use the convenience methods instead:
if let best = NSUserDefaults.standardUserDefaults().integerForKey("BestScore") {
// best will be an Int here, you can compare it to scoreForComparingWithBestScore
}
else {
// There was no BestScore value.
}
As long as you set the value with:
NSUserDefaults.setInteger(best, forKey:"BestScore")
Then because of the strong type system you will know that you have put an integer into the defaults and you then only need to check if a value was set for that key instead of also worrying about whether the object was of the correct type.
This will also drive your design, as you know that the value should be an Int rather than over optimising it is an Int8 or a UInt8.

I got here some examples for how to save NSUserDefaults and how to retrieve it. What you are seeing here is how I save text from an UITextField and how I save a UISwith. You will also see how I retrieve this information out of my NSUserDefaults.
func saveNSUserDefaults() {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(self.textFieldUrl.text + "/portal/silvertabletmenu.aspx", forKey: "url")
defaults.setBool(switchToolbar!.on, forKey: "toolbar");
defaults.synchronize()
}
func loadNSUserDefaults() {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if let urlIsNotNil = defaults.objectForKey("url") as? String {
self.textFieldUrl.text = defaults.objectForKey("url") as String
}
if textFieldUrl.text == "" {
self.textFieldUrl.text = "https://thisisjustanexample.net/"
}
switchToolbar!.on = NSUserDefaults.standardUserDefaults().boolForKey("toolbar")
}

Related

How to Define Generic “Invalid“ Values for Different Types

In my app, I am using Integers, Doubles, Floats, and CGFloats to represent a number of different values. According to my app’s semantic, these values may become “invalid“, a state which I represent using a reserved value, i. e. -1. The simplest approach to make this usable in code would be this:
anIntVariable = -1
aFloatVariable = -1
aDoubleVariable = -1.0
...
To get away from this convention driven approach and increase readability and adaptability I defined a number of extensions:
extension Int {
static var invalid = -1
}
extension Float {
static var invalid = -1.0
}
extension Double {
static var invalid = -1.0
}
...
So the above code would now read:
anIntVariable = .invalid
aFloatVariable = .invalid
aDoubleVariable = .invalid
...
It does work. However, I’m not really happy with this approach. Does anyone of you have an idea for a better way of expressing this?
To add some complexity, in addition to simple types like Int, Float, or Double, I also use Measurement based types like this:
let length = Measurement(value: .invalid, unit: UnitLength.baseUnit())
Extra bonus point if you find a way to include “invalid“ measurements in your solution as well...
Thanks for helping!
Some Additional Thoughts
I know I could use optionals with nil meaning “invalid”. In this case, however, you’d have additional overhead with conditional unwrapping... Also, using nil as “invalid” is yet another convention.
It isn’t better or worse, just different. Apple uses “invalid” values in its own APIs, i. e. the NSTableViewmethod row(for:) will return -1 if the view is not in the table view. I agree, however, that this very method perfectly illustrates that returning an optional would make a lot of sense...
I'd use optionals for that.
If you want lack of value and invalid value to be different states in your app, i'd suggest creating a wrapper for your values:
enum Validatable<T> {
case valid(T)
case invalid
}
And use it like that:
let validValue : Validatable<Int> = .valid(5)
let invalidValue : Validatable<Int> = .invalid
var validOptionalDouble : Validatable<Double?> = .valid(nil)
validOptionalDouble = .valid(5.0)
let measurement : Validatable<Measurement> = .invalid
etc.
You can then check for value by switch on that enum to access the associated value like this:
switch validatableValue {
case .valid(let value):
//do something with value
case .invalid:
//handle invalid state
}
or
if case .valid(let value) = validatableValue {
//handle valid state
}
etc

How to convert UnsafePointer<[Float]> or fit Float Array data to Unsafe[Mutable]Pointer<Float> in Swift 3?

I have an array of float value (floatArray) coming from HDF5 file, and I'd like to use this data as an input for the function that accepts Unsafe[Mutable]Pointer (inputForFunction). I'm new to UnsafePointer, so I need someone's help.
One way I have in mind is, to store the array to a file, then to open it and to map the file descriptor to the memory of the variable for the input of the function:
// read the data from HDF5 file
var floatArray: [Float] = try originalHDF5DataSet.read()
// store the data
let dataToStore = NSData(bytes: &floatArray, length: sizeOfArray * MemoryLayout<Float>.size)
let tmpPath = URL(fileURLWithPath: NSTemporaryDirectory())
let filePath = tmpPath.appendingPathComponent("floatArrayData").appendingPathExtension("dat")
do{
try data.write(to: filePath, options: .atomic)
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath:filePath.path){
print("Success")
}else{
print("Fail")
}
// open the data
let dataPath = filePath.path
let fd = open(dataPath, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
assert(fd != -1, "Error: failed to open output file at \""+dataPath+"\" errno = \(errno)\n")
// memory map the parameters
let hdr = mmap(nil, Int(sizeOfArray), PROT_READ, MAP_FILE | MAP_SHARED, fd, 0)
// cast Void pointers to Float
let inputForFunction = UnsafePointer<Float>(hdr!.assumingMemoryBound(to: Float.self))
} catch{
print("failed")
}
However, it may not be the best implementation because the process to store the data seems time-consuming.
That's why I'm considering to use UnsafePointer to pass the pointer from floatArray to inputForFunction, but I'm not sure how to implement it. Just for your information, one way I'm thinking of is to use withUnsafePointermethod directly:
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray[0]) { (ptr: UnsafePointer<Float>) in
inputForFunction = UnsafeMutablePointer(mutating: ptr)
}
or the other way is to use withMemoryRebound (but the following didn't work):
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray) { (ptrDataArray: UnsafePointer<[Float]>) in
inputForFunction = ptrDataArray.withMemoryRebound(to: inputForFunction.self, capacity: Int(sizeOfArray), { (ptr: UnsafePointer<[Float]>) -> UnsafeMutablePointer<Float> in
return ptr[0]
})
}
My question is
How to pass the address of floatArray's data to inputForFunction?
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
If needed, how to use withMemoryRebound for this purpose? Can I assign
the address/value to inputForFunction inside the closure?
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
You can find many articles explaining how you can get Unsafe(Mutable)Pointer<Float> from [Float]. Some of them answer some of your questions. Try find them later.
How to pass the address of floatArray's data to inputForFunction?
You usually use the Array's methods withUnsafeBufferPointer or withUnsafeMutableBufferPointer. You can get an Unsafe(Mutable)BufferPointer<Element> inside the closure parameter, which contains an Unsafe(Mutable)Pointer<Element> as its baseAddress property.
var floatArray: [Float] = try originalHDF5DataSet.read()
floatArray.withUnsafeBufferPointer {unsafeBufferPointer in
let inputForFunction = unsafeBufferPointer.baseAddress
//`unsafeBufferPointer` (including its `baseAddress`) is valid only in this closure.
//So, do not pass `inputForFunction` outside of the closure, use it inside.
//...
}
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
When you find something like UnsafePointer<[Float]>, then you are going the wrong way. In Swift, Array is a hidden struct which may contain (multi-level) indirect reference to its elements. You usually have no need to use UnsafePointer<Array<Float>>.
If needed, how to use withMemoryRebound for this purpose? Can I assign the address/value to inputForFunction inside the closure?
When you have an Array of AType and want to pass it as an Unsafe(Mutable)Pointer<AnotherType>, you may utilize withMemoryRebound. But in your case, you only need to work with Float, so withMemoryRebound may not be needed for your purpose.
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
None. As I wrote above, address of floatArray is just pointing somewhere which contains a hidden struct, so it's completely useless for your purpose. And address of floatArray[0] is neither guaranteed to be the address of the first element of the whole content. Swift may create a temporal region which contains only one element, and passing the address of the temporal region.
By the way, do you really need to convert your [Float] to Unsafe(Mutable)Pointer<Float>?
If your functions' signature is something like this:
func takingUnsafePointer(_ pointer: UnsafePointer<Float>)
You can call it like:
takingUnsafePointer(floatArray)
//In this case `floatArray` can be a `let` constant.
Or else, if the function signature is:
func takingUnsafeMutablePointer(_ pointer: UnsafeMutablePointer<Float>)
Call it as:
takingUnsafeMutablePointer(&floatArray)
//In this case `floatArray` needs to be a `var`, not `let`.
Remember, in both cases, the passed pointer is valid only while the function call. You should not export the pointer somewhere else.
Array type have withUnsafeBufferPointer method.
This method accept closure with UnsafeBufferPointer which is like UnsafePointer<[Float]> you're asking for.
In you need UnsafePointer to the start of Array you can use baseAddress property of UnsafeBufferPointer.
Example code:
let bufferPointer: UnsafeBufferPointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer
}
or
let baseAddress: UnsafePointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer.baseAddress
}
EDIT: Or if it's function just passing &floatArray may work.

most concise way of unwrapping and casting optional

I have a bit of code to get a string out of userDefaults:
if let userString = (userDefaults.objectForKey("user")) {
userTextField.stringValue = userString as! String
}
First, I have to see if the optional is not nil. Then I have to cast it as a string from AnyObject.
Is there a better way of doing this? maybe a one liner?
Note that your forced cast as! String will crash if a default value for the key "user" exists, but
is not a string. Generally, you can combine optional binding (if let) with an optional cast (as?):
if let userString = userDefaults.objectForKey("user") as? String {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
But actually NSUserDefaults has a dedicated method for that purpose:
if let userString = userDefaults.stringForKey("user") {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
If you want to assign a default string in the case that
the default does not exist, or is not a string, then use the
nil-coalescing operator ??, as demonstrated in
Swift issue with nil found while unwrapping an Optional value NSDefautlts, e.g.:
userTextField.stringValue = userDefaults.stringForKey("user") ?? "(Unknown)"
For the special case NSUserDefaults the best – and recommended – way is to use always non-optional values.
First register the key / value pair in AppDelegate as soon as possible but at least before using it.
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValues = ["user" : ""]
defaults.registerDefaults(defaultValues)
The benefit is you have a reliable default value of an empty string until a new value is saved the first time. In most String cases an empty string can be treated as no value and can be easily checked with the .isEmpty property
Now write just
userTextField.stringValue = userDefaults.stringForKey("user")!
Without arbitrary manipulation of the defaults property list file the value is guaranteed to be never nil and can be safely unwrapped, and when using stringForKey there is no need for type casting.
Another way that i like much to clean this up is to do each of your checks
first, and exit if any aren’t met. This allows easy understanding of what
conditions will make this function exit.
Swift has a very interesting guard statements which can also be used to avoid force unwrap crashes like :
guard let userString = userDefaults.objectForKey("user") as? String else {
// userString var will accessible outside the guard scope
return
}
userTextField.stringValue = userString
Using guards you are checking for bad cases early, making your
function more readable and easier to maintain. If the condition is not
met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically
unwrapped for you within the scope that the guard statement was
called.

Convert optional string to int in Swift

I am having troubles while converting optional string to int.
println("str_VAR = \(str_VAR)")
println(str_VAR.toInt())
Result is
str_VAR = Optional(100)
nil
And i want it to be
str_VAR = Optional(100)
100
At the time of writing, the other answers on this page used old Swift syntax. This is an update.
Convert Optional String to Int: String? -> Int
let optionalString: String? = "100"
if let string = optionalString, let myInt = Int(string) {
print("Int : \(myInt)")
}
This converts the string "100" into the integer 100 and prints the output. If optionalString were nil, hello, or 3.5, nothing would be printed.
Also consider using a guard statement.
You can unwrap it this way:
if let yourStr = str_VAR?.toInt() {
println("str_VAR = \(yourStr)") //"str_VAR = 100"
println(yourStr) //"100"
}
Refer THIS for more info.
When to use “if let”?
if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value. Let’s have a look:
if let yourStr = str_VAR?.toInt() {
println("str_VAR = \(yourStr)")
println(yourStr)
}else {
//show an alert for something else
}
The if let structure unwraps str_VAR?.toInt() (i.e. checks if there’s a value stored and takes that value) and stores its value in the yourStr constant. You can use yourStr inside the first branch of the if. Notice that inside the if you don’t need to use ? or ! anymore. It’s important to realise thatyourStr is actually of type Int that’s not an Optional type so you can use its value directly.
Try this:
if let i = str_VAR?.toInt() {
println("\(i)")
}

How do I convert a optional NSInteger to a non-optional one?

I need to retrieve a setting (with Swift):
var highScoreNumber: NSInteger = 0
var returnValue: [NSInteger]? = NSUserDefaults.standardUserDefaults().objectForKey("food") as? [NSInteger]
if (returnValue != nil) {
highScoreNumber = returnValue as NSInteger
}
I tried this and other variations of the code and I always get
'NSInteger?' not convertible to 'NSInteger'
This is my first foray into Swift, so
Am I missing something simple in the conversion?
Is there a better way to retrieve an NSInteger from settings?
When converting a nullable value to a non-nullable one, there are many good options to choose from.
But in your case, you have two issues. One is the array you’re fetching from NSUserDefaults is an array of integers, so you need to decide which one you want from that array.
If it’s the first one you want, you can use the first property. You can use optional chaining to get it. And since it’s a high score, you probably want to default to zero if it’s not present.
Here’s that in one line:
let returnValue = NSUserDefaults.standardUserDefaults().objectForKey("food") as? [Int]
let highscore = returnValue?.first ?? 0
Things to note about the above: there’s no need to give the type to the left of the = if the type is unambiguously determined by what lies to the right of the =. And it’s better to prefer Int over NSInteger.
The returnValue?.first says “if returnValue is nil, then nil, else the value of first (which, itself, returns nil if the array is empty).” The ?? says “if the left of ?? is nil, then the value on the right of nil, else the unwrapped value from the left”.
That said – do you really mean to store an array? Or do you really just want to store a single integer in the defaults and then get that out directly?
let highscore = NSUserDefaults.standardUserDefaults().integerForKey("food")
// and elsewhere in your code, when saving
NSUserDefaults.standardUserDefaults().setInteger(highscore, forKey: "food")
(integerForKey already returns a 0 default when not present rather than an optional, so no need for any unwrapping)