I'm trying to do some simple encoding using NSInputStream and NSOutputStream:
import Foundation
let path = "/Users/johni/desktop/a" // holds "123456789abcdef"
var data: NSData = NSData(contentsOfFile: path)
var inp: NSInputStream = NSInputStream(data: data)
println(data.length) // returns 15
println(inp.hasBytesAvailable) // returns false
I'm receiving a -1 from the read method, meaning that it has no bytes available, why does this happen?
I have also have tried initializing the NSInputStream directly with the fileAtPath initializer and got the same error.
You can't use an input stream until you open it.
inp.open()
inp.hasBytesAvailable //returns true
Related
I'd like to convert NSArray<NSInteger> or NSData to UnsafeMutablePointer<UInt8> for use with calling a native library. The native method in C is exposed as
typedef struct {
int64_t len;
uint8_t * _Nullable data;
} ByteBuffer;
int32_t pack(ByteBuffer request, ByteBuffer * _Nullable response, ExternError * _Nullable err);
This becomes in Swift
func pack(_ request: ByteBuffer, _ response: UnsafeMutablePointer<ByteBuffer>?, _ err: UnsafeMutablePointer<ExternError>?) -> Int32
// ByteBuffer
struct ByteBuffer {
var len: Int64
var data: UnsafeMutablePointer<UInt8>?
}
This method would be called from React Native, so my swift definition for the input method is
func pack(request: Array<NSInteger>, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void { }
I'm not making much progress, as I struggle understanding how swift works with pointers.
func pack(request: Array<NSInteger>, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
var req = ByteBuffer(len: Int64(request.count), data: nil)
req.data = UnsafeMutablePointer<UInt8>.allocate(capacity: request.count)
let ptr = UnsafePointer<UInt8>([1, 2, 3])
req.data?.assign(from: ptr, count: request.count)
pack(req, _, _)
}
I'm not sure if I'm on the right track here, I would appreciate help in any direction. The code above also produces a warning Initialization of 'UnsafePointer<UInt8>' results in a dangling pointer.
Would it be easier to just write this in Objective C instead?
I managed to get past by. Posting a solution here, in case someone else needs to work with passing pointer structures to C from Swift. The trick was to use withUnsafeBytes of NSData type or create UnsafeMutablePointer<T> using OpaquePointer. This allows getting a pointer to the underlying data, without doing a copy.
Reading back the ByteBuffer was easy just by calling the corresponding initialized of NSData.
var request = Array<NSInteger>()
request.append(16)
request.append(2)
let requestData = NSMutableData()
for i in request {
var i = i
requestData.append(&i, length: 1)
}
let req = ByteBuffer(len: Int64(requestData.count), data: UnsafeMutablePointer<UInt8>(OpaquePointer(requestData.bytes)))
// If working with NSData, instead NSMutableData, the following code can be used
let req = ByteBuffer(len: Int64(requestData.count),
data: requestData.withUnsafeBytes { body in UnsafeMutablePointer<UInt8>(mutating: body) })
var res = ByteBuffer()
var err = ExternError()
let code = didcomm_generate_key(req, &res, &err)
if code == 0 {
// Response can be obtained easily using NSData ctor
let responseData = NSData(bytesNoCopy: res.data!, length: Int(res.len), freeWhenDone: true)
}
I'm testing to connect and send a simple message to a host and I've used this guide http://studyswift.blogspot.com/2016/01/connect-mac-iphone-to-simple-python.html.
So far I'm able to connect to the server and when I tried to write on the output stream .
let data : NSData = "hello".data(using: String.Encoding.utf8)! as NSData
outStream?.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
I got error on write(UnsafePointer(data.bytes), ...
Cannot convert value of type 'UnsafeRawPointer' to expected argument type 'RawPointer'
I have successfully added https://github.com/chrisballinger/Opus-iOS to my project and I am able to call the functions declared in its header.
I want to convert from OPUS to AAC so my first step would be to decode my opus file. However, it keeps throwing an error code.
The file I am using is the 2-second file from https://people.xiph.org/~giles/2012/opus/.
This is my code
let url = Bundle.main.url(forResource: "detodos", withExtension: "opus")
print(url) //prints path correctly
var bytes = [UInt8]()
if let data = NSData(contentsOf: url!) {
var buffer = [UInt8](repeating: 0, count: data.length)
data.getBytes(&buffer, length: data.length)
bytes = buffer
let d = opus_decoder_create(48000, 1, nil)
var sampleBuffer = [opus_int16](repeating: 0, count: data.length)
print(opus_int32(bytes.count)) //6270
let w = opus_decode(d!, bytes, opus_int32(bytes.count), &sampleBuffer, opus_int32(5760), opus_int32(1))
print(sampleBuffer) // [0,0,...] every time
print(w) //-4 every time
//-4 = OPUS_INVALID_PACKET
}
I would've guessed that in this very minimal implementation nothing should go wrong but apparently it does. Printing my bytes object returns tons of different numbers so I know for a fact it doesn't stay at all-0's.
I realized that it might be due to the method expecting pure "audio data" but the file also contains a header etc. How can I strip this off?
The opus library decodes Opus packets. You give it an Opus packet and it decodes it; one packet at a time. You are trying to give it an entire Ogg Opus file, including all of the headers, framing, tags, and other metadata.
The easiest way to decode an entire Ogg Opus file is with the opusfile library. It can even stream the data from a URL if you want, so that you don't have to download it all before you start decoding as you are doing here. Alternatively, instead of opusfile you could use the ogg library to extract packets from the file and then pass each audio packet to the opus library for decoding.
I'm having a little bit of trouble. I'm translating my App to native Swift Language. I was using AS3. What I want to accomplish is to read a file in binary mode. A snippet of what I want accomplish is this:
ba:FileStream
ba.open(someFile, readMode)
ba.endian = "littleEndian"
ba.position = 128
ba.readMultiByte(4, someVariableHere)
NSData is the class you are looking for.
do {
let path = "apath"
let data = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
let buffer: UInt32 = 0
data.getBytes(&buffer, range: NSRange(location: 128,length: 4))
}
catch error {
print(error)
}
NSData documentation. NSData Class Reference
I am trying to create a multiplayer game that will send moves between players using Game Center. I'm still learning a lot about programming, so please excuse me if my question is ill-formed. Also, I am not very familiar with Obj-C, so a Swift answer would be great.
In my toy program to try and teach myself, I am trying to follow the strategy used by Shayne Meyer using the GameKitHelper class here: https://github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer
Using this approach, Shayne sends messages to other players online using structs sent as NSData. I am able to send integers (e.g., the ILoveYou message) but not messages that carry a string property (e.g., the Thanks message). In this latter case I get "Thread 1: EXC_BAD_ACCESS(code=1, address=0x78674100)" at the line "var messageThanks = UnsafePointer,MesssageThanks>(data.bytes).memory"
Eventually, I would like to send game moves that provide both strings and integers together. How does one send a message struct as NSData when properties also include a string? Secondly, I would be appreciative if someone could help me understand fundamentally what is going on when the data is packaged and how what UnsafePointer is doing as it related to sending data via Game Center.
Thank you.
Cliff
enum MessageType: Int {
case ILoveYou, Thanks
}
struct Message {
let messageType: MessageType
}
struct MessageILoveYou {
let message: Message
let messageSenderNumber: UInt32
}
struct MessageThanks {
let message: Message
let messageSenderName: String
let messageSenderNumber: UInt32
}
func sendILoveYou() {
println("sendILoveYou:")
let nameNumber = UInt32(56)
var message = MessageILoveYou(message: Message(messageType: MessageType.ILoveYou), messageSenderNumber: nameNumber)
let data = NSData(bytes: &message, length: sizeof(MessageILoveYou))
sendData(data)
}
func sendThanks() {
println("sendThanks:")
let nameString = "Don J"
let senderNumberInt = UInt32(88)
var message = MessageThanks(message: Message(messageType: MessageType.Thanks), messageSenderName: nameString, messageSenderNumber: senderNumberInt)
let data = NSData(bytes: &message, length: sizeof(MessageThanks))
sendData(data)
}
func matchReceivedData(match: GKMatch, data: NSData, fromPlayer player: String) {
println("matchReceivedData:")
var message = UnsafePointer<Message>(data.bytes).memory
if message.messageType == MessageType.ILoveYou {
println("messageType == ILoveYou")
let messageILoveYou = UnsafePointer<MessageILoveYou>(data.bytes).memory
iLoveYouThanksDelegate?.iLoveYouReceived(from: messageILoveYou.messageSenderNumber)
} else if message.messageType == MessageType.Thanks {
println("messageType == Thanks")
var messageThanks = UnsafePointer<MessageThanks>(data.bytes).memory
iLoveYouThanksDelegate?.thanksReceived(from: messageThanks.messageSenderName)
}
}
func sendData(data: NSData) {
var sendDataError: NSError?
let gameKitHelper = GameKitHelper.sharedInstance
if let multiplayerMatch = gameKitHelper.multiplayerMatch {
let success = multiplayerMatch.sendDataToAllPlayers(data, withDataMode: .Reliable, error: &sendDataError)
if !success {
if let error = sendDataError {
println("Error:\(error.localizedDescription)")
matchEnded()
}
}
}
}
The problem here is that when you create a String in Swift, it allocates a bit of memory itself, and then uses that memory to store the actual characters of the string. All that the string value really holds is some data representing a pointer to that memory and some other info (like how much memory has been allocated, so that it can be freed properly.
You can see this here:
let str = "This is quite a long string, certainly more than 24 bytes"
sizeofValue(str) // and yet this only returns 24
When you stuff variables into an NSData object, the initializer takes a pointer to the memory of the string variable that is holding those pointers, not the characters itself:
// only storing those 24 bytes, not the actual string
let data = NSData(bytes: &str, length: sizeofValue(str))
Note, the type of the bytes argument is UnsafePointer<Void>. This is an indication that you are heading into tricky territory.
Then, when you unmarshal the data at the other end, all your receiver is going to get is some pointers to random memory (sadly, memory on the other user’s device!)
If you want to put string values into an NSData object, you are going to need to marshal them first into raw data. For example, you could encode them into an array:
let data = Array(str.utf8).withUnsafeBufferPointer { buf in
NSData(bytes: buf.baseAddress, length: buf.count)
}
As it happens, since this is a common thing to want to do, there’s a method to do this directly:
let data = str.dataUsingEncoding(NSUTF8StringEncoding)
Then, to unpack the data, you can use NSString’s constructor from an NSData object:
let newStr = NSString(data: data, encoding: NSUTF8StringEncoding)
edit: if you wanted to encode more than just a string in a single NSData, you could do something along these lines… I should say, I’ve never had to do this myself so I’m in no way familiar with the standard practices for this, there could be much better techniques or helper classes/functions. Hopefully someone with more experience can edit to show how to do this properly :)
var type = MessageType.Thanks
// start the data with the type
let data = NSMutableData(bytes: &type, length: sizeofValue(type))
// then append the string
data.appendData(Array(str.utf8).withUnsafeBufferPointer { buf in
NSMutableData(bytes: buf.baseAddress, length: buf.count)
})
switch UnsafePointer<MessageType>(data.bytes).memory {
case .ILoveYou:
// ...
case .Thanks:
let str = NSString(data: data.subdataWithRange(NSMakeRange(1, data.length-1)), encoding: NSUTF8StringEncoding)
}