SwiftData (SQLite wrapper) broken with Swift 3.0 - ios

I'm using SwiftData for SQLite access.
https://github.com/mozilla-mobile/firefox-ios/blob/master/Storage/ThirdParty/SwiftData.swift
SwiftData is a SQLite wrapper coded in Swift. After Swift 3.0 and XCode 8 the following lines are broken. I'm sort of noobie with Swift so I would appreciate your help with fixing what is broken:
let text = UnsafePointer<Int8>(sqlite3_column_text(statement, index))
results to: "'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily view memory as another layout-compatible type."
return Data(bytes: UnsafePointer<UInt8>(blob), count: Int(size))
results to: "Cannot invoke initializer for type 'UnsafePointer' with an argument list of type '(UnsafeRawPointer?)'"
return sqlite3_column_int(statement, index) != 0
results to: "'!=' produces 'Bool', not the expected contextual result type 'AnyObject?'"
let text = UnsafePointer<Int8>(sqlite3_column_text(statement, index))
Results to: "'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily view memory as another layout-compatible type."
for i: Int32 in 0 ..< columnCount += 1 {
Results to: "Left side of mutating operator isn't mutable: '..<' returns immutable value"
All Help Appreciated!

sqlite3_column_int(statement, index) returns an Int32 and
sqlite3_column_int(statement, index) != 0
is a boolean value, so that makes no sense as the return value if
an (optional) AnyObject is expected. You can wrap the integer into
a NSNumber instead:
func getColumnValue(_ statement: OpaquePointer, index: Int32, type: String) -> AnyObject? {
let val = sqlite3_column_int(statement, index)
return NSNumber(value: val)
}
Another option would be
func getColumnValue(_ statement: OpaquePointer, index: Int32, type: String) -> AnyObject? {
return sqlite3_column_int(statement, index) as AnyObject
}
because in Swift 3, anything can be converted to AnyObject.
The difference is that in the second solution, the object can only be
converted back to the original number type Int32, but not to
Int or any other integer type.
For the other problems, see
Issue with UnsafePointer<Uint8> in SQLite project in Swift
Error in for loop CGFloat

Related

Type of expression is ambiguous without more context when replacing constant literal value with constant variable

I was wondering, what is a good way to fix the following compiler error?
class A {
var x: Int32
init() {
x = -1
}
}
let a: A? = nil
// No issue.
let ok: Int = Int(a?.x ?? -1)
// Type of expression is ambiguous without more context
let CONSTANT: Int = -1
let not_ok: Int = Int(a?.x ?? CONSTANT)
Any idea why we are getting Type of expression is ambiguous without more context if we use CONSTANT instead of -1?
What is a good way to fix the compiler error (retain same class, same type and same CONSTANT), yet still retain 1 liner?
It's for the same reason that you can say this:
let d = 3.0
let d2 = d + 1
But not this:
let i = 1
let d3 = d + i
Swift will cast the literal 1 to the required numeric type (here, Double), but it won't cast a variable away from its type.
So the answer is: make the types match.
The ?? operator is declared like this:
func ?? <T>(optional: T?, defaultValue: #autoclosure () throws -> T)
rethrows -> T
In the expression Int(a?.x ?? -1), the compiler needs to infer T for the
?? operator. It successfully infers T == Int32, because
the expression a?.x has the type Int32?.
it is okay for ?? to return Int32 here, because Int has an initialiser that takes Int32.
most relevantly, the literal -1 can be converted to Int32, because Int32 conforms to ExpressibleByIntegerLiteral.
If you instead do Int(a?.x ?? CONSTANT), however, that last point wouldn't work. ExpressibleByIntegerLiteral only works with literals, not any constant. The compiler sees CONSTANT as an expression of type Int, and tries very hard to find a type for the expression a?.x ?? CONSTANT, but it can't, and spits out a not-so-useful error message.
I suppose the error message is not very useful because it needs to consider quite a lot of things in this case (return type, the two parameters, overload resolution for Int.init), and it could be rather hard to pinpoint exactly where went wrong.
Anyway, to fix this, you can just convert the constant to Int32:
Int(a?.x ?? Int32(CONSTANT))
Or if you don't want to convert CONSTANT to an Int32 then back to Int again, you can rewrite this as:
a.map { Int($0.x) } ?? CONSTANT
"Map a to its x as an Int if a is not nil, otherwise CONSTANT".

Contextual type 'String' cannot be used with array literal

I am getting an error while converting my code to Swift 3.0. My sample code is below:
func hexRepresentation()->String {
let dataLength:Int = self.count
let string = NSMutableString(capacity: dataLength*2)
let dataBytes:UnsafeRawPointer = (self as NSData).bytes
for idx in 0..<dataLength {
string.appendFormat("%02x", [UInt(dataBytes[idx])] as String )
}
return string as String
}
What does this line do:
string.appendFormat("%02x", [UInt(dataBytes[idx])] as String )
First of all, it takes the byte at index idx in dataBytes and wraps it in an array. This is the array literal referred to in the error message.
Next it tries to cast the array as a String - not convert: cast. This the compiler knows to be impossible, hence the error message.
Fortunately, you don't want to convert the argument to a string because your format specifier asks for a UInt (%x), so what you really want here is just
string.appendFormat("%02x", UInt(dataBytes[idx]))
As for getting the bytes, the Swift type Data has a function foreach which is handy for iterating the bytes:
self.forEach { string.appendFormat("%02x", UInt($0)) }

Swift 3 Conversion that results in "Cannot pass immutable as inout" [duplicate]

This question already has an answer here:
Cannot pass immutable value as inout argument: function call returns immutable value
(1 answer)
Closed 6 years ago.
I'm a complete Swift newbie, but I'm writing an app for BLE using swift and have run into an issue. I'm working off of some open source code that I found in order to understand how to structure an iOS app and communicate with BLE, and when I converted it to Swift 3, a number of errors came up.
Code:
func int8Value() -> Int8 {
var value: Int8 = 0
copyBytes(to: &UInt8(value), count: MemoryLayout<Int8>.size)
return value
}
Error:
Cannot pass immutable value as inout argument: function call returns immutable value
I've been looking online for solutions to this and have found the following:
NSData to [Uint8] in Swift
CBCharacteristic value in swift3
I tried to implement these, taking a look at the following lines of code:
if let data = characteristic.value {
var bytes = Array(repeating: 0 as UInt8,count:someData.count/MemoryLayout<UInt8>.size)
data.copyBytes(to: &bytes, count:data.count)
}
and
let data = "foo".data(using: .utf8)!
let array = [UInt8](data)
let array = data.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: data.count))
}
I don't really understand the correlation between the them other than a few common variables. Can someone explain what is happening inside of the CopyBytes function (what "to" and "count" are doing), what the error is coming from, and if the examples I've been looking at have anything to do with the method I'm trying to fix?
It looks like there was an issue with the type casting from Int8 to UInt8, and taking the address of the resulting UInt8 conversion. The result of the cast is an immutable value, whose memory location cannot be passed as the function argument. If you simply initialize the variable as an unsigned int, it should pass the address just fine.
The following code should work:
func int8Value() -> Int8 {
var value: UInt8 = 0
copyBytes(to: &value, count: MemoryLayout<Int8>.size)
return Int8(value)
}

Swift: Convert Int to UInt32, and how to unwrap optional

im pretty new to swift, and im just trying to build something to test out the waters. this is in relation to a previous question i had. I am building some code to take user input from a UITextField object, and basically im trying to figure out how to convert an Int to a UInt32, but nothing ive searched on SO or otherwise has really helped.
here is my code
//this is where i call the user input.
var rangeInput: Int? {
get {
return Int(rangeInputTextField?.text ?? "")
}
//this is my function to create a range, and call a random number out of that range
let viewController = ViewController()
var x = ViewController().rangeInput
let y = (Int?(x!))
var number = arc4random_uniform(Int(y!))//ERROR OCCURS HERE "Cannot convert value of type 'Int' to expected argument type 'UInt32'
//MARK: Class for random number
struct RandomNumber {
// numberRange to change the value for 1...X(user input)
//creates the list to be picked from. (pickRandom)
func numberRange(high: UInt32) ->Range<UInt32>{
if let high = UInt32?(0){
print("Invalid number")
} else { let high = Int(y!) + 1
}
let range = 1...high
return range
}
//pick random number from that list
let pickRandom = number
}
edit: Converted UInt, using answer in comments, but am having an issue with unwrapping optionals.
am I doing something wrong with forcibly unwrapping optionals?
let viewController = ViewController()
var x = ViewController().rangeInput
var number = arc4random_uniform(UInt32(x!)) // <----- UInt32 conversion
However, do recommend to check the user input x and/or after the UInt32 conversion, in case user inputs something not sensible, using guard or if let
Initializers are called in swift by listing the data type (UInt32 in this case) followed by parenthesis containing the parameters. For most basic data types in Swift, there are no parameters for the initializers. So if you ever want to make an Int, Float, Double, UInt32, String etc., just do
UInt32(Value)!//UInt32 can be substituted for any of the above values
The "!" unwraps the optional, and will result in an error in runtime like "unexpectedly found nil when unwrapping optional value" if the value is not a valid form of the data type, if you want to do this safely, you can do this
if UInt32(Value) != nil {
//Valid Value
}else {
//Invalid Value
}
Also this is unrelated to the question, but your if let statement will always be true because you are overriding the value of the parameter "high" to a UInt32 of 0. I could be wrong though.

Swift AnyObject as String dynamic cast failed

I have an Int that I saved in Parse as an AnyObject. When I retrieve the AnyObject? and try to cast it as a String, NSString, NSNumber, or anything else, I keep getting an EXC_Breakpoint as the casting is returning Nil and there's a "Swift dynamic cast failed" error".
I tried to create this simple test to figure out which part fails, but the crazy thing is that this test will pass where seemingly all the steps are the same:
func testAnyObjectCasting(){
var myInt32: Int32 = 265
var myInt: Int = Int(myInt32)
var myAnyObject: AnyObject? = myInt as AnyObject
var myAnyArray: [[AnyObject]] = [[AnyObject]]()
myAnyArray.append(["something", myAnyObject!])
println(myAnyObject)
var myOtherAnyObject: AnyObject? = myAnyArray[0][1]
println(myOtherAnyObject)
var myString:NSNumber? = myOtherAnyObject as? NSNumber
println(myString)
var myInt2: Int = myString! as Int
}
Here's the relevant code snippets from my logic, and notes that println() works fine until the downcast to NSNumber, at which time Nil is returned:
//ABRecordGetRecordId returns an ABRecordID, which is of type Int32
//This value that's stored in the 2nd column of the multiDim [[AnyObject]]
var personId: Int = Int(ABRecordGetRecordID(person))
//This is a call to a Parse object, which returns an AnyObject. I then cast that to
//a multidimensional array AnyObject as that's the true structure of the data in swift speak
var deviceContacts: [[AnyObject]] = device?[deviceContactsFieldName] as [[AnyObject]]
//This returns the expected value, which in my broader test case is 99999, which is supported by Int
var i:Int = 1
println("the value in device contacts \(i) column 1 is: \(deviceContacts[i][1])")
//This takes a single cell value from the multidim array and puts it in an optional AnyObject
var valueInParse: AnyObject? = deviceContacts[i][1]
//This still returns 99999
println(valueInParse)
//This is where 99999 is replaced with nil. Any ideas?
var valueNSNumberInParse: NSNumber? = valueInParse as? NSNumber
//Nil :(
println(valueNSNumberInParse)
//Exception as I'm trying to unwrap nil :(
var unwrappedNSNumber:NSNumber = valueNSNumberInParse!
Part of my frustration is that I don't understand why println() works fine for AnyObject but all the casting fails. Clearly there's code that can interpret the value as a string to show for println, but that syntax eludes me for proper casting.
Because you have saved an Int into Parse and an Int is not an object it had to be converted to an object. It seems that the Parse framework has converted it to an NSValue, which is effectively a byte buffer that represents an intrinsic type.
You could try and convert these bytes back to an Int, but it is easier and better to encode the value into an NSNumber before storing it in the Parse object - then you will be easily able to handle it when you retrieve the object.

Resources