How to get Bytes from swift Data - ios

in my app I've to create some packet to send directly via network to an external device. I'm trying to create the packet.
In Objective-C I made something like this:
NSString *string = #"0021";
NSData *lengthData = [string dataUsingEncoding:NSNonLossyASCIIStringEncoding];
[data appendBytes:[lengthData bytes] length:[lengthData length]];
so in swift I made something like this:
let string = "0021"
let lengthData = string.data(using: String.Encoding.nonLossyASCII, allowLossyConversion: false)
data.append(lengthData?.bytes, count: pkt.length)
but the compiler says Cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'UnsafePointer<UInt8>', can someone help me to fix this?
Thank you

In Swift you can just append the data (assuming data is Data and var)
if let lengthData = string.data(using: .nonLossyASCII) {
data.append(lengthData)
}

Related

How to convert Objective-C NSData to Swift Data?

I have a NSData in Objective-C, the NSData has value 0x10, code like bellows:
#implementation BufUtil
+ (NSData *_Nonnull) getOCBuf {
std::vector<uint8_t> sendData = {0x10};
NSData * reqBuf = [[NSData alloc] initWithBytesNoCopy:sendData.data() length:sendData.size() freeWhenDone:false];
NSLog(#"getOCBuf, oc NSData reqBuf:%#", reqBuf);
return reqBuf;
}
#end
Then I use the data in Swift, Swift auto convert Objective-C NSData to Swift Data, but strange things happen, the value in Swift Data is 0x60, code like belows:
public func getOCBuf() -> Data {
let data = BufUtil.getOCBuf();
print("getOCBuf: swift data: \(data.hexEncodedString())")
return data
}
the log is like:
getOCBuf, oc NSData reqBuf:{length = 1, bytes = 0x10}
getOCBuf: swift data: 60
Now I am confused with what happened. Why 0x10 turn into 0x60, the two number not Binary complement.
Can anybody help me, thanks.
the code:
https://github.com/oncealong/SwiftOcDataConvert
It's my falut.
I copy code "how to convert from std::vector to NSData" from stackoverflow, but the code use [NSData alloc] initWithBytesNoCopy, which lead to all this.
the momory associate with the std::vector has freed after the getOCBuf func return. but the oc NSData and swift Data don't know. it use the origin address and found different value.
To be honest, the app should be crashed, and give a reason.

Decoding string in base 64

I need to decode a base 64 string. To do so I use the folowing code :
// Get the base 64 string vector.
NSString *vector64String = insertRequestDictionnary[#"check"];
// Decode the base 64 string into data.
NSData *vector64Data = [NSData dataFromBase64String: vector64String];
// Get the string from decoded data.
NSString *decodeVector = [[NSString alloc] initWithData: vector64Data
encoding: NSUTF8StringEncoding];
..But every time i get a nil string (decodeVector).
I check with this website (http://www.base64decode.org/), my first string (vector64string) is base 64. For example : "h508ILubppN1xLpmXWLfnw==" gives "< uĺf]bߟ"
Thanks.
Not all data is an NSUTF8String. The point of Base64 to to create a string representation of data that is not naturally a string.
NSString *vector64String = #"h508ILubppN1xLpmXWLfnw==";
NSData *vector64Data = [[NSData alloc] initWithBase64EncodedString:vector64String options:0];
NSLog(#"vector64Data: %#", vector64Data);
NSLog output:
vector64Data: <879d3c20 bb9ba693 75c4ba66 5d62df9f>
vector64Data is the decoded base64 string.
The vector64Data is not a UTF8 string, it is just data.

convert unicode string to nsstring

I have a unicode string as
{\rtf1\ansi\ansicpg1252\cocoartf1265
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;}
{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{check\}}{\leveltext\leveltemplateid1\'01\uc0\u10003 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}}
{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
\paperw11900\paperh16840\margl1440\margr1440\vieww22880\viewh16200\viewkind0
\pard\li720\fi-720\pardirnatural
\ls1\ilvl0
\f0\fs24 \cf0 {\listtext
\f1 \uc0\u10003
\f0 }One\
{\listtext
\f1 \uc0\u10003
\f0 }Two\
}
Here i have unicode data \u10003 which is equivalent to "✓" characters. I have used
[NSString stringWithCharacters:"\u10003" length:NSUTF16StringEncoding] which is throwing compilation error. Please let me know how to convert these unicode characters to "✓".
Regards,
Boom
I have same for problem and the following code solve my issue
For Encode
NSData *dataenc = [yourtext dataUsingEncoding:NSNonLossyASCIIStringEncoding];
NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding];
For decode
NSData *data = [yourtext dataUsingEncoding:NSUTF8StringEncoding];
NSString *decodevalue = [[NSString alloc] initWithData:data encoding:NSNonLossyASCIIStringEncoding];
Thanks
I have used below code to convert a Uniode string to NSString. This should work fine.
NSData *unicodedStringData =
[unicodedString dataUsingEncoding:NSUTF8StringEncoding];
NSString *emojiStringValue =
[[NSString alloc] initWithData:unicodedStringData encoding:NSNonLossyASCIIStringEncoding];
In Swift 4
let emoji = "😃"
let unicodedData = emoji.data(using: String.Encoding.utf8, allowLossyConversion: true)
let emojiString = String(data: unicodedData!, encoding: String.Encoding.utf8)
I assume that:
You are reading this RTF data from a file or other external source.
You are parsing it yourself (not using, say, AppKit's built-in RTF parser).
You have a reason why you're parsing it yourself, and that reason isn't “wait, AppKit has this built in?”.
You have come upon \u… in the input you're parsing and need to convert that to a character for further handling and/or inclusion in the output text.
You have ruled out \uc, which is a different thing (it specifies the number of non-Unicode bytes that follow the \u… sequence, if I understood the RTF spec correctly).
\u is followed by hexadecimal digits. You need to parse those to a number; that number is the Unicode code point number for the character the sequence represents. You then need to create an NSString containing that character.
If you're using NSScanner to parse the input, then (assuming you have already scanned past the \u itself) you can simply ask the scanner to scanHexInt:. Pass a pointer to an unsigned int variable.
If you're not using NSScanner, do whatever makes sense for however you're parsing it. For example, if you've converted the RTF data to a C string and are reading through it yourself, you'll want to use strtoul to parse the hex number. It'll interpret the number in whatever base you specify (in this case, 16) and then put the pointer to the next character wherever you want it.
Your unsigned int or unsigned long variable will then contain the Unicode code point value for the specified character. In the example from your question, that will be 0x10003, or U+10003.
Now, for most characters, you could simply assign that over to a unichar variable and create an NSString from that. That won't work here: unichars only go up to 0xFFFF, and this code point is higher than that (in technical terms, it's outside the Basic Multilingual Plane).
Fortunately, *CF*String has a function to help you:
unsigned int codePoint = /*…*/;
unichar characters[2];
NSUInteger numCharacters = 0;
if (CFStringGetSurrogatePairForLongCharacter(codePoint, characters)) {
numCharacters = 2;
} else {
characters[0] = codePoint;
numCharacters = 1;
}
You can then use stringWithCharacters:length: to create an NSString from this array of 16-bit characters.
Use this:
NSString *myUnicodeString = #"\u10003";
Thanks to modern Objective C.
Let me know if its not what you want.
NSString *strUnicodeString = "\u2714";
NSData *unicodedStringData = [strUnicodeString dataUsingEncoding:NSUTF8StringEncoding];
NSString *emojiStringValue = [[NSString alloc] initWithData:unicodedStringData encoding:NSUTF8StringEncoding];

JSON Processing in iOS: Removing newlines

I am trying to decode json being sent to my application from Twitter's Streaming API.
Twitter will send my client data which has the following structure:
The body of a streaming API response consists of a series of newline-delimited messages, where "newline" is considered to be \r\n (in hex, 0x0D 0x0A) and "message" is a JSON encoded data structure or a blank line.
They basically separate tweets with \r\n. Sometimes they send you one tweet, sometimes they send you many at once.
Decoding the 'many' case is problematic for me. I use NSJSONSerialization class built into iOS. The problem is that it reads the first tweet and then see's the \r\n and considers this as garbage and returns an empty array and accompanying error.
To get around this problem, I use the following code to basically remove the newlines.
// json is the received json data
NSString* responseString = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding];
NSMutableArray* responseMutableArray = [[responseString componentsSeparatedByString:#"\r\n"] mutableCopy];
[responseMutableArray removeLastObject];
NSArray* responseArray = [NSArray arrayWithArray:responseMutableArray];
[responseMutableArray removeAllObjects];
NSError* error;
for(NSString* tweetString in responseArray)
{
NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:[tweetString dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
[responseMutableArray addObject:dict];
}
//responseMutableArray contains the parsed tweets
The approach is, convert the data into a NSString, NSString into an array (by spliting by \r\n), array back into data and then parse the JSON into a dictionary. Its a messy process and I wonder if there is something more efficient way that I can do this.
Thanks very much
Vb
Disclaimer: I actually got here by encountering this issue myself.
I think the simplest solution would be a revision of #Elmundo 's answer that removes the newlines completely. It worked for me.
Note: this is Swift version 3.something
guard var string = String(data: data, encoding: .utf8) else {
return
}
string = string.stringByReplacingOccurrencesOfString("\r\n", withString: "")
guard let data = string.data(using: .utf8) else {
return false
}
do {
let json = try NSJSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
}catch let e as NSError {
print(e.description)
}
Try this:
NSString *string = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding]:
string = [NSString stringWithFormat:#"[%#]", [string stringByReplacingOccurrencesOfString:#"\\r\\n" withString:#","]];
NSArray *array= [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
Touch this:
let stringData = String(data: data, encoding: NSUTF8StringEncoding)
if let strData = stringData {
let newString = strData.stringByReplacingOccurrencesOfString("\r\n", withString: "\\r\\n")
if let newString = newString {
do {
let dict:[AnyObject] = try NSJSONSerialization.JSONObjectWithData(newString.dataUsingEncoding(NSUTF8StringEncoding)!, options: [.AllowFragments, .MutableContainers]) as! [AnyObject]
}catch let e as NSError {
print(e.description)
}
}
}

Convert UTF-8 encoded NSData to NSString

I have UTF-8 encoded NSData from windows server and I want to convert it to NSString for iPhone. Since data contains characters (like a degree symbol) which have different values on both platforms, how do I convert data to string?
If the data is not null-terminated, you should use -initWithData:encoding:
NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
If the data is null-terminated, you should instead use -stringWithUTF8String: to avoid the extra \0 at the end.
NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];
(Note that if the input is not properly UTF-8-encoded, you will get nil.)
Swift variant:
let newStr = String(data: data, encoding: .utf8)
// note that `newStr` is a `String?`, not a `String`.
If the data is null-terminated, you could go though the safe way which is remove the that null character, or the unsafe way similar to the Objective-C version above.
// safe way, provided data is \0-terminated
let newStr1 = String(data: data.subdata(in: 0 ..< data.count - 1), encoding: .utf8)
// unsafe way, provided data is \0-terminated
let newStr2 = data.withUnsafeBytes(String.init(utf8String:))
You could call this method
+(id)stringWithUTF8String:(const char *)bytes.
I humbly submit a category to make this less annoying:
#interface NSData (EasyUTF8)
// Safely decode the bytes into a UTF8 string
- (NSString *)asUTF8String;
#end
and
#implementation NSData (EasyUTF8)
- (NSString *)asUTF8String {
return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}
#end
(Note that if you're not using ARC you'll need an autorelease there.)
Now instead of the appallingly verbose:
NSData *data = ...
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
You can do:
NSData *data = ...
[data asUTF8String];
The Swift version from String to Data and back to String:
Xcode 10.1 • Swift 4.2.1
extension Data {
var string: String? {
return String(data: self, encoding: .utf8)
}
}
extension StringProtocol {
var data: Data {
return Data(utf8)
}
}
extension String {
var base64Decoded: Data? {
return Data(base64Encoded: self)
}
}
Playground
let string = "Hello World" // "Hello World"
let stringData = string.data // 11 bytes
let base64EncodedString = stringData.base64EncodedString() // "SGVsbG8gV29ybGQ="
let stringFromData = stringData.string // "Hello World"
let base64String = "SGVsbG8gV29ybGQ="
if let data = base64String.base64Decoded {
print(data) // 11 bytes
print(data.base64EncodedString()) // "SGVsbG8gV29ybGQ="
print(data.string ?? "nil") // "Hello World"
}
let stringWithAccent = "Olá Mundo" // "Olá Mundo"
print(stringWithAccent.count) // "9"
let stringWithAccentData = stringWithAccent.data // "10 bytes" note: an extra byte for the acute accent
let stringWithAccentFromData = stringWithAccentData.string // "Olá Mundo\n"
Sometimes, the methods in the other answers don't work. In my case, I'm generating a signature with my RSA private key and the result is NSData. I found that this seems to work:
Objective-C
NSData *signature;
NSString *signatureString = [signature base64EncodedStringWithOptions:0];
Swift
let signatureString = signature.base64EncodedStringWithOptions(nil)
Just to summarize, here's a complete answer, that worked for me.
My problem was that when I used
[NSString stringWithUTF8String:(char *)data.bytes];
The string I got was unpredictable: Around 70% it did contain the expected value, but too often it resulted with Null or even worse: garbaged at the end of the string.
After some digging I switched to
[[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];
And got the expected result every time.
With Swift 5, you can use String's init(data:encoding:) initializer in order to convert a Data instance into a String instance using UTF-8. init(data:encoding:) has the following declaration:
init?(data: Data, encoding: String.Encoding)
Returns a String initialized by converting given data into Unicode characters using a given encoding.
The following Playground code shows how to use it:
import Foundation
let json = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""
let data = json.data(using: String.Encoding.utf8)!
let optionalString = String(data: data, encoding: String.Encoding.utf8)
print(String(describing: optionalString))
/*
prints:
Optional("{\n\"firstName\" : \"John\",\n\"lastName\" : \"Doe\"\n}")
*/

Resources