I receive binary information via stream in Swift. Lets say the information is a picture. I now want to save the picture. How is this possible?
I tried following:
let bufferSize = 154000
var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
var bytesRead = inputStream?.read(&buffer, maxLength: bufferSize)
if bytesRead > 0 {
var bytesWrittenSoFar = 0
do {
var diffbytes = bytesRead! - bytesWrittenSoFar
fileStream?.open()
fileStream?.write(UnsafePointer(&buffer[bytesWrittenSoFar]), maxLength: diffbytes)
} while (bytesWrittenSoFar != bytesRead);
But when I try to write (fileStream?.write...) I get following error: "Could not find an overload for 'init' that accepts the supplied arguments
Thank you for your answer in advance!
The problem is with the initialization of UnsafePointer. In this case, you don't need it at all, you can just pass &buffer[bytesWrittenSoFar], as that is an acceptable value to pass to a function that needs an unsafePointer, per the discussion in the apple book "Using Swift with Cocoa and Objective-C".
Related
I'm trying to write a native messaging host for a chrome/firefox extension in GJS (since it will rely on code already written in GJS) but encountering some hurdles. I'm using chrome-gnome-shell as a rough template since it also uses GLib/Gio instrospection and GApplication, but it has the advantage of python struct that I don't have.
Quickly, native messaging hosts exchange messages through stdin/stdout which are an Int32 (4-bytes) length following by a string of utf-8 encoded JSON.
chrome-gnome-shell uses GLib.IOChannel with set_encoding('utf-8') and struct to handle int32 bytes. I've had trouble using that class in GJS and don't have struct so have been trying Gio.UnixInputStream wrapped in Gio.DataInputStream (and output counterparts), with put_int32()/read_int32() and put_string()/read_string().
Apparently I'm mightily confused about what I'm doing. If I call Gio.DataInputStream.read_int32() it returns a number 369098752, so I'm guessing the int32 is not being converted to a regular Number. If I call Gio.DataInputStream.read_bytes(4, null).unref_to_array() to get a ByteArray; ByteArray.toString() returns '\u0016' while ByteArray[0] returns '22' which appears to be the actual length.
Some pointers on reading/writing int32's to a datastream and would be much appreciated.
chrome-gnome-shell references:
on_input()
send_message()
I don't know if this is the best way to solve this, but here's what I came up with.
Two functions using the ByteArray import (modified from somewhere on SO):
const ByteArray = imports.byteArray;
function fromInt32 (byteArray) {
var value = 0;
for (var i = byteArray.length - 1; i >= 0; i--) {
value = (value * 256) + byteArray[i];
}
return value;
};
function toInt32 (num) {
var byteArray = [0, 0, 0, 0];
for (var index_ = 0; index_ < byteArray.length; index_++) {
var byte = num & 0xff;
byteArray [index_] = byte;
num = (num - byte) / 256 ;
}
return ByteArray.fromArray(byteArray);
};
For receiving/sending:
const Gio = imports.gi.Gio;
// Receiving
let stdin = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({ fd: 0 })
});
let int32 = stdin.read_bytes(4, null).toArray();
let length = fromInt32(int32);
let data = stdin.read_bytes(length, null).toArray().toString();
let message = JSON.parse(data);
// Sending
let stdout = new Gio.DataOutputStream({
base_stream: new Gio.UnixOutputStream({ fd: 1 })
});
let data = JSON.stringify(message);
let int32 = toInt32(data.length);
stdout.write(int32, null);
stdout.put_string(data, null);
Of course, you should wrap these in try-catch as appropriate and you'll probably want to connect a source to the input (you can use the Gio.UnixInputStream):
let source = stdin.base_stream.create_source(null);
source.set_callback(onReceiveFunc);
source.attach(null);
You may be able to use Gio.DataOutputStream.put_int32() and Gio.DataInputStream.read_int32() the same way as you use read_bytes() and put_string().
In objective c, I use fscanf to read stream from file and assign the value to variables:
int count;
char type[5];
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
I want to do the same thing in swift code, I tried:
//ERROR: Type annotation missing in pattern
//What type should I use for `count`?
var count
//ERROR: consecutive statement on a line must be separated by ‘;’
var type[5] : char
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
But I got compiler errors showing above. What is the correct way to use fscanf in swift ?
If you know any swift way to achieve the same thing (without using fscanf), it would be great too!
I recommend you use Foundation framework solution for reading/writing file data. A sample code to read contents of files which I used in my app to stream file into NSData:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(5)
var chars = [UInt8](count: 5, repeatedValue: 0)
data.getBytes(&chars, length: 5)
fileHandle.closeFile()
}
In case you need read Int64 data from file at a specific location:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(500)
var intFetched: Int64 = 0
let location = 100 // start at 101st character of file
data.getBytes(&intFetched, range: NSMakeRange(location, 8))
println(intFetched.littleEndian)
fileHandle.closeFile()
}
I'm using GCDAsyncSocket. Write an application, which is used to transfer files between two devices, have a few problems when receiving the file:
When I want to send a size of about 60 KB text file to another receiving devices(iOS devices or iOS Simulator), need to add some packet information before data, like this:
2 bytes header version,
1 byte identify data or command,
4 bytes data size,
25 bytes the reserved space,
After 32 bytes is file data, text files or image files or other files.
So, before I call the socket.writeData, I write a NSMutableData,and send to receiver device.
var sendData: NSMutableData = NSMutableData()
var fileData: NSData = NSData(contentsOfFile: "file path", options: NSDataReadingOptions.DataReadingMapped, error: &error)
// TCP_HEADER_VERSION is Int16, value is 1
var headerVersion = TCP_HEADER_VERSION.bigEndian
var unPackVersionData: NSData = NSData(bytes: &headerVersion, length: 2)
sendData.appendData(unPackVersionData)
// dataType is Int, value is 0
var dataTypeTemp = dataType
sendData.appendData(NSData(bytes: &dataTypeTemp, length: 1))
// dataSize is Int64, value is fileData.size
var dataSizeTemp = dataSize.bigEndian
var packSizeData: NSData = NSData(bytes: &dataSizeTemp, length: 4)
sendData.appendData(packSizeData)
sendData.appendData(NSData(bytes: [0] as Array<Int>, length: 25))
sendData.appendData(fileData)
// send data
socket?.writeData(data, withTimeout: -1, tag: 0)
socket?.readDataWithTimeout(-1, tag: 0)
Every thing is ok at send device, the problem at receiver device
Received four packets. Every packet size is different, but I just send once at sender device, and socket header is chaotic.
// receiver device
func socket(sock: GCDAsyncSocket!, didReadData data: NSData!, withTag tag: Int) {
sock.readDataWithTimeout(-1, tag: 0)
println(data)
// PACKET_HEADER_LENGTH is 32
var packetHeader: NSData = data.subdataWithRange(NSMakeRange(0, PACKET_HEADER_LENGTH))
var packetContent: NSData = data.subdataWithRange(NSMakeRange(PACKET_HEADER_LENGTH, data.length - PACKET_HEADER_LENGTH))
var headerVersion: Int16 = 0
headerPacket.getBytes(&headerVersion, range: NSMakeRange(0, 2))
// headerVersion is wrong, not 1
headerVersion = headerVersion.bigEndian
// packetType is wrong, not 0
var packetType: Int = 0
headerPacket.getBytes(&packetType, range: NSMakeRange(2, 1))
var packetSize: Int64 = 0
headerPacket.getBytes(&packetSize, range: NSMakeRange(3, 4))
packetSize = packetSize.bigEndian
}
And I found some discussion about read data:
Having trouble programming streams
In end of the data add [GCDAsyncSocket CRLFData], at didReadData before use data add readDataToData:[GCDAsyncSocket CRLFData], but also not solve the problem.
Anyone can help me? Thanks!
Well.. I know how to solve this problem.
When I send packet to receiver, is like this:
64kb ... send
64kb ... send
64kb ... send
// ... send other datas to finish
At receiver device:
64kb ... waiting other datas
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
// clear buffer
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
So, When receive datas, need save a buffer file ,when send finish, handle buffer file to real file.
If anyone know how to set GCDAsyncSocket buffer size, please tell me.
Hope I can help others guys, thanks.
I am trying to use the UUID to generate as a nonce to be use for Twitter reverse authentication. But apparently the UUID is not a good choice. So how can I generate a unique random string every time stripping out all non-word characters, taking care that it gets release from memory after use. The following code crashes.
var uuid: CFUUIDRef = CFUUIDCreate(nil)
var nonce: CFStringRef = CFUUIDCreateString(nil, uuid)
CFRelease(uuid)
println("createdNonce:\(nonce)")
EDIT:
I am on xcode6 beta2, and i can't debug, and xocde crashes, any chance it gets. So well, the CFRelease part is crashing for me. Once I remove, it seems to work fine, but I dont know if this will create a memory leak.
As to why UUID's might not be a good choice to use for nonce it seems is because, UUID's are not made of true random bits, referring this discussion here: https://github.com/aws/aws-sdk-ios/issues/30
A more correct way of generation a nonce would probably be to generate random bytes using a cryptographic RNG. iOS just happens to have such a thing:
var s = NSMutableData(length: 32)
SecRandomCopyBytes(kSecRandomDefault, UInt(s.length), UnsafePointer<UInt8>(s.mutableBytes))
// s is now a NSData containing 32 random bytes
Then convert to a string using whatever format the API suggests (probably Base64), e.g.
let base64str = s.base64EncodedStringWithOptions(0)
EDIT: The approach above seems to be the one Twitter uses in the docs. See here. I can't say if UUIDS are more easily predicted. It depends on the method they are generated. SecRandomCopyBytes seems to be used for cryptographic purposes though, so it should be safe to use.
With SwiftUI 5.x and CryptoKit, it is easy:
import CryptoKit
let nonce = ChaChaPoly.Nonce.init()
in use:
let encryptedContent = try! ChaChaPoly.seal(inputBuffer, using: symmetricKey, nonce: ChaChaPoly.Nonce.init()).combined
One example that's provided by Firebase's Authenticating Using Apple tutorial:
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: Array<Character> =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
}
return random
}
randoms.forEach { random in
if remainingLength == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
Your code is crashing because you are calling CFRelease.In swift there is no need to call CFRelease.From swift guide
Core Foundation objects returned from annotated APIs are automatically
memory managed in Swift—you do not need to invoke the CFRetain,
CFRelease, or CFAutorelease functions yourself.
var uuid: CFUUIDRef = CFUUIDCreate(nil)
var nonce: CFStringRef = CFUUIDCreateString(nil, uuid)
//Remove this swift will manage the memory managment.This line is causing crash
//CFRelease(uuid)
println("createdNonce:\(nonce)")
this code will work fine.No this will not create memory leak swift will manage that
Here is an answer that works in Swift 4:
func generateNonce(lenght: Int) throws -> Data {
let nonce = NSMutableData(length: lenght)
let result = SecRandomCopyBytes(kSecRandomDefault, nonce!.length, nonce!.mutableBytes)
if result == errSecSuccess {
return nonce! as Data
} else {
throw Error
}
}
This might not be a perfect solution but want to suggest it because it will take a lot of Twitter reverse auth madness for you..
Try to use cocoapod TWReverseAuth
Here is a easier way to do this then:
var temp = NSUUID.UUID().UUIDString
var nonce = temp.stringByReplacingOccurrencesOfString("-", withString: "")
println("createdNonce:\(nonce)")
I'm on beta 3. Consider the following Objective-C line:
const uint8_t *reportData = [data bytes];
where data is a NSData object.
How would this line be re-written in Swift?
data.bytes is of type ConstUnsafePointer<()>, and while there's plenty of documentation on how to create a pointer type in Swift, there isn't much info on how to work with them.
edit:
To add some context, I'm trying to port Apple's HeartRateMonitor sample code to Swift. This code interacts with BLE heart rate monitors. This code I'm working on translates the data received by the Bluetooth system into an int for use in the UI. The data received from BT is expected to be an array of uints, element 0 is used to check for a flag and element 1 contains the value.
Here's the same Objective-C line in context:
const uint8_t *reportData = [data bytes];
uint16_t bpm = 0;
if ((reportData[0] & 0x01) == 0)
{
/* uint8 bpm */
bpm = reportData[1];
}
What you were looking for was how to convert NSData to an array of UInt8. Here's how.
import Foundation
let path = "/etc/csh.cshrc" // something existent
let data = NSData(contentsOfFile: path)
var aofb = [UInt8](count:data.length, repeatedValue:0)
data.getBytes(&aofb, length:data.length)
for c in aofb {
let s = UnicodeScalar(Int(c)).escape(asASCII:true)
println("\(c):\(s)")
}
Just built following code (Note code below works on Beta 3, ConstUnsafePointer<()> needs to be changed to COpaquePointer in order to work on Beta 2, please see edit history for more information)
var dataPath = NSBundle.mainBundle().pathForResource("TestData", ofType: "") // What I have in TestData is "GREETINGS WORLD"
var originalData = NSData(contentsOfFile: dataPath)
var dataLength = originalData.length
println("original data: \(originalData)") // Output original data
// Data to bytes
var reportBytes: ConstUnsafePointer<()> = originalData.bytes
var bytesToString = NSString(bytes: reportBytes, length: dataLength, encoding: NSUTF8StringEncoding)
println("string from bytes: \(bytesToString)")
// Bytes to data
var bytesToData = NSData(bytes: reportBytes, length: dataLength)
println("data from bytes: \(bytesToData)")
Console log
original data: <47524545 54494e47 5320574f 524c44>
string from bytes: GREETINGS WORLD
data from bytes: <47524545 54494e47 5320574f 524c44>
Also found this may help
ConstUnsafePointer<T>
/// This type stores a pointer to an object of type T. It provides no
/// automated memory management, and therefore the user must take care
/// to allocate and free memory appropriately.
Hope this shed light.
Looking at handling bluetooth heart rate monitors in Swift now I found the simplest way to get the NSData byte values to UInt8 format:
let bytes = UnsafePointer<UInt8>(data.bytes)
if bytes[0] & 0x01 == 0 {
NSLog("BPM \(bytes[1]")
}