I am implementing push notifications. I'd like to save my APNS Token as a String.
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken
{
NSString *tokenString = [NSString stringWithUTF8String:[newDeviceToken bytes]]; //[[NSString alloc]initWithData:newDeviceToken encoding:NSUTF8StringEncoding];
NSLog(#"%#", tokenString);
NSLog(#"%#", newDeviceToken);
}
The first line of code prints null. the second prints the token. How can I get my newDeviceToken as an NSString?
If anyone is looking for a way to do this in Swift:
Swift 3 introduces the Data type, with value semantics. To convert the deviceToken to a String, you can do as follows:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print(token)
}
Old answer using NSData:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
print("tokenString: \(tokenString)")
}
Someone Helped me with this.I am just passing along
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const unsigned *tokenBytes = [deviceToken bytes];
NSString *hexToken = [NSString stringWithFormat:#"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
[[MyModel sharedModel] setApnsToken:hexToken];
}
You could use this
- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
const char *data = [deviceToken bytes];
NSMutableString *token = [NSMutableString string];
for (NSUInteger i = 0; i < [deviceToken length]; i++) {
[token appendFormat:#"%02.2hhX", data[i]];
}
return [token copy];
}
For those who want in Swift 3 and most easier method
func extractTokenFromData(deviceToken:Data) -> String {
let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
return token.uppercased();
}
Note - This will not work when compiling with the iOS 13 or later SDK
use this :
NSString * deviceTokenString = [[[[deviceToken description]
stringByReplacingOccurrencesOfString: #"<" withString: #""]
stringByReplacingOccurrencesOfString: #">" withString: #""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
NSLog(#"The generated device token string is : %#",deviceTokenString);
Explanation of %02.2hhx in the high vote answer:
%: Introduces the x conversion specifier.
02: The minimum width of the converted value is 2. If the converted value has fewer bytes than the field width, it shall be padded with 0 on the left.
.2: Gives the minimum number of digits to appear for the x conversion specifier.
hh: Specifies that the x conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing).
x: The unsigned argument shall be converted to unsigned hexadecimal format in the style "dddd"; the letters "abcdef" are used. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it shall be expanded with leading zeros. The default precision is 1. The result of converting zero with an explicit precision of zero shall be no characters.
For more details, see the IEEE printf specification.
Based on the above explanation, I think it is better to change %02.2hhx to %02x or %.2x.
For Swift 5, the following methods are all feasible:
deviceToken.map({String(format: "%02x", $0)}).joined()
deviceToken.map({String(format: "%.2x", $0)}).joined()
deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
deviceToken.reduce("", {$0 + String(format: "%.2x", $1)})
The test is as follows:
let deviceToken = (0..<32).reduce(Data(), {$0 + [$1]})
print(deviceToken.reduce("", {$0 + String(format: "%.2x", $1)}))
// Print content:
// 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
In iOS 13 the description will be in different format. Kindly use below code to fetch the device token.
- (NSString *)fetchDeviceToken:(NSData *)deviceToken {
NSUInteger len = deviceToken.length;
if (len == 0) {
return nil;
}
const unsigned char *buffer = deviceToken.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(len * 2)];
for (int i = 0; i < len; ++i) {
[hexString appendFormat:#"%02x", buffer[i]];
}
return [hexString copy];
}
It's my solution and It works well in my app:
NSString* newToken = [[[NSString stringWithFormat:#"%#",deviceToken]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]] stringByReplacingOccurrencesOfString:#" " withString:#""];
convert NSData to NSString with stringWithFormat
trim the "<>"
remove the spaces
I think converting deviceToken to hex byte string has no sense. Why? You will send it to your backend, where it will be transformed back to bytes to be pushed to APNS. So, use NSData's method base64EncodedStringWithOptions, push it to server, and then use reverse base64decoded data :) That is so much easier :)
NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
In iOS 13 description will break so use this
let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
For clarity, let’s break this down and explain each part:
The map method operates on each element of a sequence. Because Data is a sequence of bytes in Swift, the passed closure is evaluated for each byte in deviceToken.
The String(format:) initializer evaluates each byte in the data (represented by the anonymous parameter $0) using the %02x format specifier, to produce a zero-padded, 2-digit hexadecimal representation of the byte / 8-bit integer.
After collecting each byte representation created by the map method, joined() concatenates each element into a single string.
P.S don't use description gives different string in iOS 12 and iOS 13 and not safe as per future scope. Developers shouldn’t have relied on a specific format for an object’s description.
// iOS 12
(deviceToken as NSData).description // "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"
// iOS 13
(deviceToken as NSData).description // "{length = 32, bytes = 0x965b251c 6cb1926d e3cb366f dfb16ddd ... 5f857679 376eab7c }"
For more information read This.
2020
token as text...
let tat = deviceToken.map{ data in String(format: "%02.2hhx", data) }.joined()
or if you prefer
let tat2 = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
(result is the same)
This is a little bit shorter solution:
NSData *token = // ...
const uint64_t *tokenBytes = token.bytes;
NSString *hex = [NSString stringWithFormat:#"%016llx%016llx%016llx%016llx",
ntohll(tokenBytes[0]), ntohll(tokenBytes[1]),
ntohll(tokenBytes[2]), ntohll(tokenBytes[3])];
Functional Swift version
One liner:
let hexString = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes),
count: data.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
Here's in a reusable and self documenting extension form:
extension NSData {
func base16EncodedString(uppercase uppercase: Bool = false) -> String {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes),
count: self.length)
let hexFormat = uppercase ? "X" : "x"
let formatString = "%02\(hexFormat)"
let bytesAsHexStrings = buffer.map {
String(format: formatString, $0)
}
return bytesAsHexStrings.joinWithSeparator("")
}
}
Alternatively, use reduce("", combine: +) instead of joinWithSeparator("") to be seen as a functional master by your peers.
Edit: I changed String($0, radix: 16) to String(format: "%02x", $0), because one digit numbers needed to having a padding zero
(I don't know yet how to mark a question as a duplicate of this other one, so I just posted my answer again)
Throwing my answer on the pile. Avoid using string parsing; It's not guaranteed by the docs that NSData.description will always work that way.
Swift 3 Implementation:
extension Data {
func hexString() -> String {
var bytesPointer: UnsafeBufferPointer<UInt8> = UnsafeBufferPointer(start: nil, count: 0)
self.withUnsafeBytes { (bytes) in
bytesPointer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(bytes), count:self.count)
}
let hexBytes = bytesPointer.map { return String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}
This will work for you,
NSUInteger dataLength = deviceToken.length;
const unsigned char *dataBuffer = (const unsigned char *)deviceToken.bytes;
NSMutableString *deviceTokenString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
[deviceTokenString appendFormat:#"%02x", dataBuffer[i]];
}
NSLog(#"The generated device token string is : %#",deviceTokenString);
I've tried to test two different methods with format "%02.2hhx" and "%02x"
var i :Int = 0
var j: Int = 0
let e: Int = Int(1e4)
let time = NSDate.timeIntervalSinceReferenceDate
while i < e {
_ = deviceToken.map { String(format: "%02x", $0) }.joined()
i += 1
}
let time2 = NSDate.timeIntervalSinceReferenceDate
let delta = time2-time
print(delta)
let time3 = NSDate.timeIntervalSinceReferenceDate
while j < e {
_ = deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
j += 1
}
let time4 = NSDate.timeIntervalSinceReferenceDate
let delta2 = time4-time3
print(delta2)
and the result is that the fastest is "%02x" at average 2.0 vs 2.6 for the reduced version:
deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
Here's how you do it in Xamarin.iOS
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var tokenStringBase64 = deviceToken.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
//now you can store it for later use in local storage
}
Using updateAccumulatingResult is more efficient than the various other approaches found here, so here's the Swiftiest way to stringify your Data bytes:
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.reduce(into: "") { $0 += String(format: "%.2x", $1) }
print(token)
}
For Swift :
var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
var deviceTokenString: String = ( deviceToken.description as NSString )
.stringByTrimmingCharactersInSet( characterSet )
.stringByReplacingOccurrencesOfString( " ", withString: "" ) as String
println( deviceTokenString )
NSString *tokenString = [[newDeviceToken description] stringByReplacingOccurrencesOfString:#"[<> ]" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, [[newDeviceToken description] length])];
Swift:
let tokenString = deviceToken.description.stringByReplacingOccurrencesOfString("[ <>]", withString: "", options: .RegularExpressionSearch, range: nil)
What about one line solution?
Objective C
NSString *token = [[data.description componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet]invertedSet]]componentsJoinedByString:#""];
Swift
let token = data.description.components(separatedBy: CharacterSet.alphanumerics.inverted).joined()
-(NSString *)deviceTokenWithData:(NSData *)data
{
NSString *deviceToken = [[data description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
deviceToken = [deviceToken stringByReplacingOccurrencesOfString:#" " withString:#""];
return deviceToken;
}
Swift
// make sure that we have token for the devie on the App
func application(application: UIApplication
, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
var tokenStr = deviceToken.description
tokenStr = tokenStr.stringByReplacingOccurrencesOfString("<", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(">", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)
print("my token is: \(tokenStr)")
}
Use excellent category!
// .h file
#interface NSData (DeviceToken)
- (NSString *)stringDeviceToken;
#end
// .m file
#import "NSData+DeviceToken.h"
#implementation NSData (DeviceToken)
- (NSString *)stringDeviceToken {
const unsigned *deviceTokenBytes = [deviceToken bytes];
NSString *deviceToken = [NSString stringWithFormat:#"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(deviceTokenBytes[0]), ntohl(deviceTokenBytes[1]), ntohl(deviceTokenBytes[2]),
ntohl(deviceTokenBytes[3]), ntohl(deviceTokenBytes[4]), ntohl(deviceTokenBytes[5]),
ntohl(deviceTokenBytes[6]), ntohl(deviceTokenBytes[7])];
return deviceToken;
}
#end
// AppDelegate.m
#import "NSData+DeviceToken.h"
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *token = deviceToken.stringDeviceToken;
}
Works fine!
Swift 3:
If any one is looking for a way to get device token in Swift 3. Use the below modified snippet.
let characterSet: CharacterSet = CharacterSet( charactersIn: "<>" )
let deviceTokenString: String = (deviceToken.description as NSString)
.trimmingCharacters(in: characterSet as CharacterSet)
.replacingOccurrences(of: " ", with: "")
.uppercased()
print(deviceTokenString)
var token: String = ""
for i in 0..<deviceToken.count {
token += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
}
print(token)
The solution #kulss posted here, while lacking in elegance but having the virtue of simplicity no longer works in iOS 13, since description will work differently for NSData. You can still use debugDescription though.
NSString * deviceTokenString = [[[[deviceToken debugDescription]
stringByReplacingOccurrencesOfString: #"<" withString: #""]
stringByReplacingOccurrencesOfString: #">" withString: #""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
Try this one unless the data is null-terminated.
NSString* newStr = [[NSString alloc] initWithData:newDeviceToken
encoding:NSUTF8StringEncoding];
NSString *tokenstring = [[NSString alloc] initWithData:token encoding:NSUTF8StringEncoding];
My first question was how to change the Font of a word example "test" in a textView, and it was answered correctly by #bkrl and #Torongo.
func changeAllOccurence(of string: String, font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
var range = NSMakeRange(0, attributedString.mutableString.length)
while(range.location != NSNotFound)
{
range = attributedString.mutableString.range(of: string, options: .caseInsensitive, range: range)
if(range.location != NSNotFound)
{
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: range)
range = NSMakeRange(range.location + range.length, self.characters.count - (range.location + range.length));
}
}
return attributedString
}
As I am still not familiar with the above code, I tried to add few lines in order to generalize the code so it can works for an array of strings and not only one string.
But for sure it didn't works cause it changed the font of the last word only which is reasonable cause the final changes will be for the last word which is "usage":
let words = ["example", "usage"]
for word in words {
let attributedText = text.changeAllOccurence(of: word, font: UIFont.boldSystemFont(ofSize: 17))
textview.attributedText = attributedText
}
Can someone advise how to improve the code provided by #Toromgo in order to work for any array of strings instead of just one?
You could create extension like:
extension String {
func change(font: UIFont, of string: String) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
let subStringRange = attributedString.mutableString.range(of: string,
options: .caseInsensitive)
if subStringRange.location != NSNotFound {
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: subStringRange)
}
return attributedString
}
}
Usage:
let text = "This is example of usage extension"
let attributedText = text.change(font: UIFont.boldSystemFont(ofSize: 17), of: "example")
textView.attributedText = attributedText
Hope that helps!
As you have changed your question, ihave updated my answer accordingly. Please try this:
extension String {
func changeAllOccurence(of strings: [String], font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: self)
for eachString in strings {
var range = NSMakeRange(0, attributedString.mutableString.length)
while(range.location != NSNotFound)
{
range = attributedString.mutableString.range(of: eachString, options: .caseInsensitive, range: range)
if(range.location != NSNotFound)
{
attributedString.addAttribute(NSFontAttributeName,
value: font,
range: range)
range = NSMakeRange(range.location + range.length, self.characters.count - (range.location + range.length));
}
}
}
return attributedString
}
}
I have run the code and it is working.
So if you have an NSString that goes:
#"My blue car is bigger than my blue shoes or my blue bicycle";
I would like a method that replaces only the first instance of blue with green, to produce:
#"My green car is bigger than my blue shoes or my blue bicycle";
How does one do this?
Assuming the following inputs:
NSString *myString = #"My blue car is bigger then my blue shoes or my blue bicycle";
NSString *original = #"blue";
NSString *replacement = #"green";
The algorithm is quite simple:
NSRange rOriginal = [myString rangeOfString:original];
if (NSNotFound != rOriginal.location) {
myString = [myString stringByReplacingCharactersInRange:rOriginal withString:replacement];
}
SWIFT 3 and 4 UPDATE:
extension String
{
func stringByReplacingFirstOccurrenceOfString(
target: String, withString replaceString: String) -> String
{
if let range = self.range(of: target) {
return self.replacingCharacters(in: range, with: replaceString)
}
return self
}
}
A more Swift'y version of the implementations posted here. This one matches the syntax replacingOccurrences(of:with:) in Swift 5.1
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
guard let range = self.range(of: target) else { return self }
return self.replacingCharacters(in: range, with: replacement)
}
}
An example of usage:
let string = "I like bacon, please give me some more bacon!"
let newString = string.replacingFirstOccurrence(of: "bacon", with: "meat")
Swift 4 version:
func replacingFirstOccurrence(of string: String, with replacement: String) -> String {
guard let range = self.range(of: string) else { return self }
return replacingCharacters(in: range, with: replacement)
}
In Swift is useful extends String in this way:
public extension String {
func stringByReplacingFirstOccurrenceOfString(target: String, withString replaceString: String) -> String {
if let range = self.rangeOfString(target) {
return self.stringByReplacingCharactersInRange(range, withString: replaceString)
}
return self
}
}
In this way you can call wherever:
let s = "The red red cat".stringByReplacingFirstOccurrenceOfString("red", withString: "fat")
print(s) // "The fat red cat"
NSString *initialString = #"My blue car is bigger then my blue shoes or my blue bicycle";
NSRange range = [initialString rangeOfString:#"blue"];
NSString *replacedString = [initialString stringByReplacingCharactersInRange:range withString:#"green"];
NSLog(#"replacedString: %#", replacedString);
First locate the substring, and then make the replacement. Example :
NSString *aString = #"foo bar foo";
NSRange firstFooRange = [aString rangeOfString:#"foo"];
NSString *anotherString = [aString stringByReplacingOccurrencesOfString:#"foo"
withString:#"bar"
options:0
range:firstFooRange];
NSString documentation.
-(NSString*) replaceFirstOccuarnceFromString: (NSString*)input withOriginal:(NSString*) original AndReplacment:(NSString*)replacement
{
NSRange rOriginal = [input rangeOfString: original];
if (NSNotFound != rOriginal.location) {
input = [input
stringByReplacingCharactersInRange: rOriginal
withString: replacement];
}
return input;
}