Code that was previously working in Swift 2.2 is now throwing the following error in Swift 3:
Here is my code:
let tempData: NSMutableData = NSMutableData(length: 26)!
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes:data.bytes)
What should I replace "data.bytes" with to fix the error? I've tried implementing 'withUnsafeBytes' and had a look at Apple's documentation, but can't get my head around it!
Assuming that data has type Data, the following should work:
let tempData: NSMutableData = NSMutableData(length: 26)!
data.withUnsafeBytes {
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes: $0)
}
using the
/// Access the bytes in the data.
///
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
public func withUnsafeBytes<ResultType, ContentType>(_ body: #noescape (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType
method of Data. Inside the closure $0 is a UnsafePointer<Void>
to the bytes (UnsafeRawPointer in Xcode 8 beta 6).
Use this
:
let data:Data = Data()
let myDataBytes = data.withUnsafeBytes {_ in
UnsafePointer<UInt8>(data.withUnsafeBytes({
$0
}))
}
let writtenBytes = writeStream.write(.init(myDataBytes), maxLength: data.count)
Related
With Swift 3 leaning towards Data instead of [UInt8], I'm trying to ferret out what the most efficient/idiomatic way to encode/decode swifts various number types (UInt8, Double, Float, Int64, etc) as Data objects.
There's this answer for using [UInt8], but it seems to be using various pointer APIs that I can't find on Data.
I'd like to basically some custom extensions that look something like:
let input = 42.13 // implicit Double
let bytes = input.data
let roundtrip = bytes.to(Double) // --> 42.13
The part that really eludes me, I've looked through a bunch of the docs, is how I can get some sort of pointer thing (OpaquePointer or BufferPointer or UnsafePointer?) from any basic struct (which all of the numbers are). In C, I would just slap an ampersand in front of it, and there ya go.
Note: The code has been updated for Swift 5 (Xcode 10.2) now. (Swift 3 and Swift 4.2 versions can be found in the edit history.) Also possibly unaligned data is now correctly handled.
How to create Data from a value
As of Swift 4.2, data can be created from a value simply with
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
Explanation:
withUnsafeBytes(of: value)
invokes the closure with a buffer pointer covering the raw bytes of the value.
A raw buffer pointer is a sequence of bytes, therefore Data($0) can be used to create the data.
How to retrieve a value from Data
As of Swift 5, the withUnsafeBytes(_:) of Data invokes the closure with an “untyped” UnsafeMutableRawBufferPointer to the bytes. The load(fromByteOffset:as:) method the reads the value from the memory:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
let value = data.withUnsafeBytes {
$0.load(as: Double.self)
}
print(value) // 42.13
There is one problem with this approach: It requires that the memory is property aligned for the type (here: aligned to a 8-byte address). But that is not guaranteed, e.g. if the data was obtained as a slice of another Data value.
It is therefore safer to copy the bytes to the value:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
Explanation:
withUnsafeMutableBytes(of:_:) invokes the closure with a mutable buffer pointer covering the raw bytes of the value.
The copyBytes(to:) method of DataProtocol (to which Data conforms) copies bytes from the data to that buffer.
The return value of copyBytes() is the number of bytes copied. It is equal to the size of the destination buffer, or less if the data does not contain enough bytes.
Generic solution #1
The above conversions can now easily be implemented as generic methods of struct Data:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
The constraint T: ExpressibleByIntegerLiteral is added here so that we can easily initialize the value to “zero” – that is not really a restriction because this method can be used with “trival” (integer and floating point) types anyway, see below.
Example:
let value = 42.13 // implicit Double
let data = Data(from: value)
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = data.to(type: Double.self) {
print(roundtrip) // 42.13
} else {
print("not enough data")
}
Similarly, you can convert arrays to Data and back:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
Example:
let value: [Int16] = [1, Int16.max, Int16.min]
let data = Data(fromArray: value)
print(data as NSData) // <0100ff7f 0080>
let roundtrip = data.toArray(type: Int16.self)
print(roundtrip) // [1, 32767, -32768]
Generic solution #2
The above approach has one disadvantage: It actually works only with "trivial"
types like integers and floating point types. "Complex" types like Array
and String have (hidden) pointers to the underlying storage and cannot be
passed around by just copying the struct itself. It also would not work with
reference types which are just pointers to the real object storage.
So solve that problem, one can
Define a protocol which defines the methods for converting to Data and back:
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
Implement the conversions as default methods in a protocol extension:
extension DataConvertible where Self: ExpressibleByIntegerLiteral{
init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}
I have chosen a failable initializer here which checks that the number of bytes provided
matches the size of the type.
And finally declare conformance to all types which can safely be converted to Data and back:
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
This makes the conversion even more elegant:
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
The advantage of the second approach is that you cannot inadvertently do unsafe conversions. The disadvantage is that you have to list all "safe" types explicitly.
You could also implement the protocol for other types which require a non-trivial conversion, such as:
extension String: DataConvertible {
init?(data: Data) {
self.init(data: data, encoding: .utf8)
}
var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}
or implement the conversion methods in your own types to do whatever is
necessary so serialize and deserialize a value.
Byte order
No byte order conversion is done in the above methods, the data is always in
the host byte order. For a platform independent representation (e.g.
“big endian” aka “network” byte order), use the corresponding integer
properties resp. initializers. For example:
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
Of course this conversion can also be done generally, in the generic
conversion method.
You can get an unsafe pointer to mutable objects by using withUnsafePointer:
withUnsafePointer(&input) { /* $0 is your pointer */ }
I don't know of a way to get one for immutable objects, because the inout operator only works on mutable objects.
This is demonstrated in the answer that you've linked to.
In my case, Martin R's answer helped but the result was inverted. So I did a small change in his code:
extension UInt16 : DataConvertible {
init?(data: Data) {
guard data.count == MemoryLayout<UInt16>.size else {
return nil
}
self = data.withUnsafeBytes { $0.pointee }
}
var data: Data {
var value = CFSwapInt16HostToBig(self)//Acho que o padrao do IOS 'e LittleEndian, pois os bytes estavao ao contrario
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}
The problem is related with LittleEndian and BigEndian.
I'm trying to convert my string into SHA256 hash, but I get the next error:
Cannot invoke initializer for type 'UnsafeMutablePointer<UInt8>' with an argument list of type '(UnsafeMutableRawPointer)'
That's my function:
func SHA256(data: String) -> Data {
var hash = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))!
if let newData: Data = data.data(using: .utf8) {
let bytes = newData.withUnsafeBytes {(bytes: UnsafePointer<CChar>) -> Void in
CC_SHA256(bytes, CC_LONG(newData.length), UnsafeMutablePointer<UInt8>(hash.mutableBytes))
}
}
return hash as Data
}
so, for this part
UnsafeMutablePointer<UInt8>(hash.mutableBytes)
I get this error:
Cannot invoke initializer for type 'UnsafeMutablePointer<UInt8>' with an argument list of type '(UnsafeMutableRawPointer)'
How can I fix that and what I do wrong?
You'd better use Data also for the result hash.
In Swift 3, withUnsafePointer(_:) and withUnsafeMutablePointer(:_) are generic types and Swift can infer the Pointee types correctly when used with "well-formed" closures, which means you have no need to convert Pointee types manually.
func withUnsafeBytes((UnsafePointer) -> ResultType)
func withUnsafeMutableBytes((UnsafeMutablePointer) -> ResultType)
func SHA256(data: String) -> Data {
var hash = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
if let newData: Data = data.data(using: .utf8) {
_ = hash.withUnsafeMutableBytes {mutableBytes in
newData.withUnsafeBytes {bytes in
CC_SHA256(bytes, CC_LONG(newData.count), mutableBytes)
}
}
}
return hash
}
In Swift 3, the initializers of UnsafePointer and UnsafeMutablePointer, which was used to convert Pointee types till Swift 2, are removed. But in many cases, you can work without type conversions of pointers.
I'm upgrading code from Swift 2 to Swift 3 and ran across this error:
wordcount.swift:7:5: error: value of type 'String' has no member 'enumerateSubstringsInRange'
line.enumerateSubstringsInRange(range, options: .ByWords) {w,,,_ in
In Swift 2, this method comes from a String extension of which the compiler is aware.
I have not been able to locate this method in the Swift 3 library. It appears in the documentation for Foundation here:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/occ/instm/NSString/enumerateSubstringsInRange:options:usingBlock:
My entire script is:
import Foundation
var counts = [String: Int]()
while let line = readLine()?.lowercased() {
let range = line.characters.indices
line.enumerateSubstringsInRange(range, options: .ByWords) {w,_,_,_ in
guard let word = w else {return}
counts[word] = (counts[word] ?? 0) + 1
}
}
for (word, count) in (counts.sorted {$0.0 < $1.0}) {
print("\(word) \(count)")
}
It works with Swift 2.2 (modulo the changes I have already made for Swift 3, such as lowercase -> lowercased and sort -> sorted) but fails to compile with Swift 3.
And very strangely, neither the Swift 3 command line compiler nor the Swift Migration assistant in XCode 8 Beta suggests a replacement, as it does for many other renamed methods. Perhaps enumerateSubstringsInRange is deprecated or its parameter names changed?
If you type str.enumerateSubstrings in a Playground, you'll see the following as a completion option:
enumerateSubstrings(in: Range<Index>, options: EnumerationOptions, body: (substring: String?, substringRange: Range<Index>, enclosingRange: Range<Index>, inout Bool) -> ())
In addition to addressing the new enumerateSubstrings(in:options:body:) syntax, you need to also change how you get the range for the string:
import Foundation
var counts = [String: Int]()
while let line = readLine()?.lowercased() {
let range = line.startIndex ..< line.endIndex
line.enumerateSubstrings(in: range, options: .byWords) {w,_,_,_ in
guard let word = w else {return}
counts[word] = (counts[word] ?? 0) + 1
}
}
for (word, count) in (counts.sorted {$0.0 < $1.0}) {
print("\(word) \(count)")
}
I'm working on a Multiplayer Swift (XCode 7.3 / iOS 9.2) game using GameKit.
I'm using a custom struct as the information sent via GameKit. Copy of the struct is below. frustrationNetworkMessage is a String enum & Card is a seperate class (can post code if needed)
struct frustrationNetworkMessage {
var messageType : FrustrationNetworkMessageType
var cards : [Card]
var actingPlayer : String
init(messageType : FrustrationNetworkMessageType, cards : [Card], actingPlayer : String) {
self.messageType = messageType
self.cards = cards
self.actingPlayer = actingPlayer
}
My issue is in the 'match(match: GKMatch, didReceiveData data: NSData, fromRemotePlayer player: GKPlayer)' method. I've worked out this is being called once some data is received:
private var receivedData : NSMutableData
func decode<T>(data: NSData) -> T {
let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
data.getBytes(pointer, length: sizeof(T))
return pointer.move()
}
func match(match: GKMatch, didReceiveData data: NSData, fromRemotePlayer player: GKPlayer) {
print("Match / did receive data: size of data - \(sizeofValue(data))")
receivedData.appendData(data)
if let receivedNetworkMessage : frustrationNetworkMessage = decode(receivedData) {
print("Match / did receive data: \(receivedNetworkMessage.messageDetails())")
When I run the above code it crashes at the final print statement with EXC_BAD_ACCESS (which I understand is it complaining it's trying to access part of the struct that has not been received).
As an example:
Message summary at sent -> sendNetworkMessage: Message type: startNewGame with actingPlayer: G:118279601 & card count: 1 - message size: 40
Message length first time match didReceiveData is called -> Match / did receive data: size of data - 8
How can I check I've gotten the entire struct before acting on it? I had thought of updating the struct to include a length field (in theory I should always receive that in one go - so could use it to check the packet length).
You can use a background process like this to check your data structure continuously?
newQueue = NSOperationQueue()
let operation2 = NSBlockOperation(block: {
})
operation2.completionBlock = {
print("Operation 2 completed")
}
let operation1 = NSBlockOperation(block: {
})
operation1.completionBlock = {
self.newQueue.addOperation(operation2)
}
operation1.qualityOfService = .UserInitiated
newQueue.addOperation(operation1)
}
Initialise the variables and then check their length; this will save/stop it crashing.
I've found an answer for the minute (issue has now moved elsewhere in the code)
struct frustrationNetworkMessage {
var messageType : FrustrationNetworkMessageType
var card : Card
var actingPlayer : String
init(messageType : FrustrationNetworkMessageType, card : Card, actingPlayer : String) {
self.messageType = messageType
self.card = card
self.actingPlayer = actingPlayer
}
init(data : NSData) {
let messageTypeLength = sizeofValue(FrustrationNetworkMessageType)
let cardLength = sizeofValue(Card)
let messageTypeRange = NSMakeRange(0, messageTypeLength)
let cardLengthRange = NSMakeRange(messageTypeLength, messageTypeLength + cardLength)
let actingPlayerRange = NSMakeRange(messageTypeLength + cardLength, data.length)
self.messageType = .didDiscardCard
self.card = Card.emptyCard()
data.getBytes(&self.messageType, range: messageTypeRange)
data.getBytes(&self.card, range: cardLengthRange)
let actingPlayerData = data.subdataWithRange(actingPlayerRange)
self.actingPlayer = NSString(data: actingPlayerData, encoding: NSUTF8StringEncoding) as! String
}
func archive()->NSData {
var localMessageType = messageType.rawValue
var localCard = card
let returnValue = NSMutableData(bytes: &localMessageType, length: sizeofValue(self.messageType.rawValue))
returnValue.appendData(NSData(bytes: &localCard, length: sizeofValue(self.card)))
returnValue.appendData(actingPlayer.dataUsingEncoding(NSUTF8StringEncoding)!)
return returnValue
}
What I've found through trial an error (and a lot of follow-up reading):
match didReceiveData only seems to get called when it has received an entire packet (so don't need to handle receiving partial data)
Where possible avoid encoding class data types as NSData (through digging I found I was only encoding a local address, works fine if passing locally - not so much over the network). Close to all of my data types as either basic ones (all enums are now of type UInt8) or structs.
I'm trying to create an class to retrieve JSON from a web API in Swift. Instead of going with delegates, I thought of using closures but I'm struggling with a few things.
let login = {(email: String, password: String) -> (Int, String) in
let response = { (response: NSHTTPURLResponse!, data: HTTPHandler.Data!, error: NSError!) -> Void in
var value: String? = response.allHeaderFields[HTTPHeaderFields.token] as AnyObject? as? String
var headerData = value?.dataUsingEncoding(NSASCIIStringEncoding)
var values: NSArray = NSJSONSerialization.JSONObjectWithData(headerData, options: .AllowFragments, error: nil) as NSArray
println(values)
return (values[0], values[1]) // Tuple types '(AnyObject!, AnyObject!)' and '()' have a different number of elements (2 vs. 0)
}
let httpHandler = HTTPHandler.POST(SERVER_URL + POSTendpoints.login, data: ["email": email, "password": password], response: response)
return nil // Type '(Int, String)' does not conform to protocol 'NilLiteralConvertible'
}
Here is the closure I wrote. It accepts two parameters (email, password) and should return a tuple (User ID, API Token). I have a separate class called HTTPHandler which calls the server and I get and can parse the response successfully. Here's the example JSON output.
(
2,
JDJ5JDEwJFZ4ZkR4eXlkYUxiYS93TXUwbjBtbnUzaVhidFZBUVVtMTRJM0J3WFFBemszSVVjZ3RWd05h
)
But I can't return the value in a tuple. I get following two errors. I've commented them in the above code snippet where they occur.
I'm still struggling with closure syntax in Swift so I'm not entirely sure if I have done this right. I've defined the closure as a constant. That way can I call this closure from another class? If not, how can I make it possible to do so?
Can someone please help me to resolve these issues?
Thank you.
First of all, in order to return nil you need to make your closure return an optional tuple. Use (Int, String)? or (Int, String)! instead of (Int, String)
You're getting the error "Tuple types '(AnyObject!, AnyObject!)' and '()' have a different number of elements" because you're trying to return a (Int, String) tuple instead of Void which is what the closure returns.
I can't test it myself right now, but it would look something like this:
let login = {(email: String, password: String) -> (Int, String)? in
var returnTuple: (Int, String)? = nil
let response = { (response: NSHTTPURLResponse!, data: HTTPHandler.Data!, error: NSError!) -> Void in
var value: String? = response.allHeaderFields[HTTPHeaderFields.token] as AnyObject? as? String
var headerData = value?.dataUsingEncoding(NSASCIIStringEncoding)
var values: NSArray = NSJSONSerialization.JSONObjectWithData(headerData, options: .AllowFragments, error: nil) as NSArray
println(values)
returnTuple = (values[0], values[1])
}
let httpHandler = HTTPHandler.POST(SERVER_URL + POSTendpoints.login, data: ["email": email, "password": password], response: response)
return returnTuple
}
Regarding the first error, it seems that you should cast values elements to proper types:
return (values[0] as Int, values[1] as String)
Regarding the second error, non-optional type just can't be nil in Swift, if you want to indicate absence of value, you should change return type of your closure to (Int, String)?.