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

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)
}

Related

Data value Overflow

I am a little confused on how Data object works in swift. I am attempting to write values using bluetooth and one of the values I need to write is 500. In order to write you need to convert the value to a data object before sending. Here's the code I am using.
if(characteristic != nil){
var byteCount = 1
if(sensitivity > 255){
byteCount = 2
}
let data = Data.init(bytes: &sensitivity, count: byteCount)
peripheral.writeValue(data, for: sensCharacteristic!, type: CBCharacteristicWriteType.withResponse)
peripheral.readValue(for: sensCharacteristic!)
}else{
print("No bluetooth Connection")
}
From what I understood if I try to send a value that takes more than a byte to represent then it will overflow, thus when I send 500 the value it writes is actually 244. Because of this I tried writing and sending 2 bytes but the value I am getting is 244101. I am unsure where that value is coming from. What is the correct way to covert 500?
This function is called whenever a characteristic is read and then placed in a label. All it does is loop through the data and adds each byte to a string.
func readCharacteristic(data: Data) -> String {
var characterString = ""
for byte in data {
let c = String(byte)
characterString.append(c)
}
return characterString
}
Based on this SO post: round trip Swift number types to/from Data (pointed to by Wei Jay)...
Try this:
func readCharacteristic(data: Data) -> Int {
return data.withUnsafeBytes { $0.pointee }
}
Note: that post provides various ways to convert the Data to different data types - Int, Float, Double, etc - so this could be used if you know you'll only be reading an Int value. Otherwise, just build on this + the other post.

SwiftData (SQLite wrapper) broken with Swift 3.0

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

UnsafePointer<UInt8> initializer in Swift 3

I have a receipt validation class that is deprecated since Swift 3 has released. I fixed some issues, but I still have many ...
Here is the GitHub source code I used : https://gist.github.com/baileysh9/4386ea92b047d97c7285#file-parsing_productids-swift and https://gist.github.com/baileysh9/eddcba49d544635b3cf5
First Error :
var p = UnsafePointer<UInt8>(data.bytes)
Compiler throws : Cannot invoke initializer for type UnsafePointer(UInt8) with an argument list of type UnsafeRawPointer
Second error
while (ptr < end)
Binary operators < cannot be applied to two UnsafePointer(UInt8) operands
Thank you very much in advance :)
EDIT
Thanks to LinShiwei answer I found a solution to UnsafePointer declaration. It compiles but not tested yet (because other errors avoid me to test) :
func getProductIdFromReceipt(_ data:Data) -> String?
{
let tempData: NSMutableData = NSMutableData(length: 26)!
data.withUnsafeBytes {
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes: $0)
}
var p: UnsafePointer? = tempData.bytes.assumingMemoryBound(to: UInt8.self)
In Swift 3, you cannot init an UnsafePointer using an UnsafeRawPointer.
You can use assumingMemoryBound(to:) to convert an UnsafeRawPointer into an UnsafePointer<T>. Like this:
var ptr = data.bytes.assumingMemoryBound(to: UInt8.self)
Use debugDescription or distance(to:) to compare two pointer.
while(ptr.debugDescription < endPtr.debugDescription)
or
while(ptr.distance(to:endPtr) > 0)
It is possible to put a regular pointer sign i C & in front of a Int8 array or Uint8 array to make a pointer to supply a C-function input. Like the &aBuffer array below (but the data array must copied locally first, to keep control of the storage of the data storage until finished with the operation, you get an error else). Here in a routine handling dropInteraction data (delivering a byte array):
func interpretInstanceData(filename: String, Buffer: [UInt8]) -> String {
var aBuffer = Buffer
let sInstanceData = String(cString: Ios_C_InterpretInstanceData(filename, &aBuffer, Int32(aBuffer.count)))
The question is slightly old. But googling for a solution on the topic of converting a swift byte array to a C-pointer (what else is UnsafePointer< UInt8 >). This question made a hit. But I think this answer is helpful for later editions of Swift (that I use). Would have worked even then. Worked in any kind of use needing a pointer from swift (just make the array of right type first).
May have recently changed to just this, without the ".bytes." part:
var p: UnsafePointer = data.assumingMemoryBound(to: UInt8.self)
from the original:
var p = UnsafePointer<UInt8>(data.bytes)

Converting C pointers to Swift 3

I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(

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.

Resources