How can I create a modified Base64 encoded UUID in Swift? - ios

I am interested in creating a modified encoded UUID but I am having a heck of a time getting the right format. What I currently have does not compile for reasons unknown but oddly Im able to run it in a playground. Here is the function:
class func genID() -> String {
let uuid = NSUUID.UUID()
let data = NSData(bytes:&uuid, length:16)
let base64 = data.base64EncodedStringWithOptions(nil)
let options = NSStringCompareOptions.LiteralSearch
let result = base64
.stringByReplacingOccurrencesOfString("/", withString: "-", options: options, range: nil)
.stringByReplacingOccurrencesOfString("+", withString: "_", options: options, range: nil)
.stringByReplacingOccurrencesOfString("=", withString: "", options: options, range: nil)
return result
}
The problem is flagged on the second line of the function; the creation of the data value. Strangely that line does work in a playground. Any help would be appreciated. So what am I missing here?
Thanks in advance.

You can create an NSData object from NSUUID with
let uuid = NSUUID.UUID()
// data object with appropriate size:
let data = NSMutableData(length: 16)
// fill the bytes from the UUID:
uuid.getUUIDBytes(UnsafeMutablePointer(data.mutableBytes))

I was able to do this without using NSUUID by converting the uuid_t type to a Data object. This type is an alias for a 16-byte tuple. Looks a little strange in the implementation, but it works, and this is fairly future proof if for some reason the uuid_t type changes. Here's the UUID extension I created:
import Foundation
extension UUID {
var base64String: String {
return self.data.base64EncodedString()
}
var data: Data {
var result = Data()
let uuidTuple = self.uuid
// NOTE: There are clever ways to iterate over a tuple in Swift,
// but I actually want this to not compile if the implementation of uuid_t
// changes in the future
result.append(uuidTuple.0)
result.append(uuidTuple.1)
result.append(uuidTuple.2)
result.append(uuidTuple.3)
result.append(uuidTuple.4)
result.append(uuidTuple.5)
result.append(uuidTuple.6)
result.append(uuidTuple.7)
result.append(uuidTuple.8)
result.append(uuidTuple.9)
result.append(uuidTuple.10)
result.append(uuidTuple.11)
result.append(uuidTuple.12)
result.append(uuidTuple.13)
result.append(uuidTuple.14)
result.append(uuidTuple.15)
return result
}
}

Related

Put a string before a certain string inside a text in Swift 3

I'm dealing with an issue here, I have a URL of an image which is like this
http://example.com/image/test.jpg
Which is a string.
And I would like to insert before .jpg a certain text like -40x40
Is there any way to analyze the URL string and somehow to add this text so the final string should be
http://example.com/image/text-40x40.jpg
What i've tried till now is this
var finalImage = "http://example.com/image/test.jpg"
finalImage.insert("-40x40" as Character, at: finalImage.endIndex - 4)
but i get 2 errors.
1) i cant add more than 1 character and 2) i cant do the math ad endIndex.
But i can't add more than one character there.
Thanks a lot!
Try this. It uses NSURL, which exists so that path manipulations are easy and legal! The documentation is really quite good.
let s1 = "http://example.com/image/test.jpg"
let u = URL(fileURLWithPath: s1)
let exExt = u.deletingPathExtension()
let s2 = exExt.absoluteString + "-40x40.jpg"
Another ways.
The code shown in the question, fixed:
var finalImage = "http://example.com/image/test.jpg"
let extIndex = finalImage.index(finalImage.endIndex, offsetBy: -4)
finalImage.insert(contentsOf: "-40x40".characters, at: extIndex)
Using NSRegularExpression:
let origImage = "http://example.com/image/test.jpg"
let regex = try! NSRegularExpression(pattern: "(\\.jpg)$", options: .caseInsensitive)
let finalImage = regex.stringByReplacingMatches(in: origImage, range: NSRange(0..<origImage.utf16.count), withTemplate: "-40x40$0")
As a complement to the neat accepted answer by #Grimxn: Foundation's URL has various more methods that allows for more separation "of concerns" in case you'd like to apply some more complex modification of the image (file) name, while not really worrying about the image (file) name extension.
let s1 = "http://example.com/something.cgi/image/test.jpg"
let u = URL(fileURLWithPath: s1)
// separate into (String) components of interest
let prefixUrl = u.deletingLastPathComponent().absoluteString
// "http://example.com/something.cgi/image/"
let fileName = u.deletingPathExtension().lastPathComponent
// "test"
let fileExtension = "." + u.pathExtension
// ".jpg"
// ... some methods that implements your possibly more
// complex filename modification
func modify(fileName fName: String) -> String {
// ...
return fName + "-40x40"
}
// reconstruct url with modified filename
let s2 = prefixUrl + modify(fileName: fileName) + fileExtension
print(s2) // http:/example.com/something.cgi/image/test-40x40.jpg
Grimxn's solution is probably the best fit for this problem, but for more complex manipulation of URLs, take a look at the NSURLComponents class. You can convert an NSURL to NSURLComponents, then use the various methods of NSURLComponents to manipulate your URL, and then finally convert the NSURLComponents back to an NSURL
As noted in a comment by #dfri, Swift 3 (and later) includes a native URLComponents class, which follows Swift naming and calling conventions. Going forward you should use that instead of the Objective-C/Swift 2 NSURLComponents class.
if let url = NSURL(string: "http://example.com/image/test.jpg"),withoutExt = url.URLByDeletingPathExtension
{
let finalstring : NSString = withoutExt.absoluteString + "-40x40.jpg"
print(finalstring)
}
Very simply with one line...
let newURL = oldURL.stringByReplacingOccurrencesOfString(".jpg", withString: "-40x40.jpg", options: NSStringCompareOptions.LiteralSearch, range: nil)

NSNetService dictionaryFromTXTRecord fails an assertion on invalid input

The input to dictionary(fromTXTRecord:) comes from the network, potentially from outside the app, or even the device. However, Apple's docs say:
... Fails an assertion if txtData cannot be represented as an NSDictionary object.
Failing an assertion leaves the programmer (me) with no way of handling the error, which seems illogic for a method that processes external data.
If I run this in Terminal on a Mac:
dns-sd -R 'My Service Name' _myservice._tcp local 4567 asdf asdf
my app, running in an iPhone, crashes.
dictionary(fromTXTRecord:) expects the TXT record data (asdf asdf) to be in key=val form. If, like above, a word doesn't contain any = the method won't be able to parse it and fail the assertion.
I see no way of solving this problem other than not using that method at all and implementing my own parsing, which feels wrong.
Am I missing something?
Here's a solution in Swift 4.2, assuming the TXT record has only strings:
/// Decode the TXT record as a string dictionary, or [:] if the data is malformed
public func dictionary(fromTXTRecord txtData: Data) -> [String: String] {
var result = [String: String]()
var data = txtData
while !data.isEmpty {
// The first byte of each record is its length, so prefix that much data
let recordLength = Int(data.removeFirst())
guard data.count >= recordLength else { return [:] }
let recordData = data[..<(data.startIndex + recordLength)]
data = data.dropFirst(recordLength)
guard let record = String(bytes: recordData, encoding: .utf8) else { return [:] }
// The format of the entry is "key=value"
// (According to the reference implementation, = is optional if there is no value,
// and any equals signs after the first are part of the value.)
// `ommittingEmptySubsequences` is necessary otherwise an empty string will crash the next line
let keyValue = record.split(separator: "=", maxSplits: 1, omittingEmptySubsequences: false)
let key = String(keyValue[0])
// If there's no value, make the value the empty string
switch keyValue.count {
case 1:
result[key] = ""
case 2:
result[key] = String(keyValue[1])
default:
fatalError()
}
}
return result
}
I'm still hoping there's something I'm missing here, but in the mean time, I ended up checking the data for correctness and only then calling Apple's own method.
Here's my workaround:
func dictionaryFromTXTRecordData(data: NSData) -> [String:NSData] {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length)
var pos = 0
while pos < buffer.count {
let len = Int(buffer[pos])
if len > (buffer.count - pos + 1) {
return [:]
}
let subdata = data.subdataWithRange(NSRange(location: pos + 1, length: len))
guard let substring = String(data: subdata, encoding: NSUTF8StringEncoding) else {
return [:]
}
if !substring.containsString("=") {
return [:]
}
pos = pos + len + 1
}
return NSNetService.dictionaryFromTXTRecordData(data)
}
I'm using Swift 2 here. All contributions are welcome. Swift 3 versions, Objective-C versions, improvements, corrections.
I just ran into this one using Swift 3. In my case the problem only occurred when I used NetService.dictionary(fromTXTRecord:) but did not occur when I switched to Objective-C and called NSNetService dictionaryFromTXTRecord:. When the Objective-C call encounters an entry without an equal sign it creates a key containing the data and shoves it into the dictionary with an NSNull value. From what I can tell the Swift version then enumerates that dictionary and throws a fit when it sees the NSNull. My solution was to add an Objective-C file and a utility function that calls dictionaryFromTXTRecord: and cleans up the results before handing them back to my Swift code.

Swift: how to censor/filter text entered for swear words, etc?

I just want to see whether there is an established way to do this, or how one would go about this.
I have a text field that essentially acts as a form in my iOs app where a user can post something. I can't have users posting swear words/inappropriate crap, so I want to filter out and display an error if the string they enter contains one of these words.
How do other apps in Swift do this? Do they just search through the string to see if it contains the word (obviously not within other words, but standing alone) or is there another method?
How can I accurately filter out the swear words from my user post in Swift?
Construct a list of words you consider to be swear words, and simply check the user entered string whether any of these words are contained within the string.
Swift 3:
import Foundation
func containsSwearWord(text: String, swearWords: [String]) -> Bool {
return swearWords
.reduce(false) { $0 || text.contains($1.lowercased()) }
}
// example usage
let listOfSwearWords = ["darn", "crap", "newb"]
/* list as lower case */
let userEnteredText1 = "This darn didelo thread is a no no."
let userEnteredText2 = "This fine didelo thread is a go."
print(containsSwearWord(text: userEnteredText1, swearWords: listOfSwearWords)) // true
print(containsSwearWord(text: userEnteredText2, swearWords: listOfSwearWords)) // false
Swift 2.2:
import Foundation
func containsSwearWord(text: String, swearWords: [String]) -> Bool {
return swearWords
.reduce(false) { $0 || text.containsString($1.lowercaseString) }
}
// example usage
let listOfSwearWords = ["darn", "crap", "newb"]
/* list as lower case */
let userEnteredText1 = "This darn didelo thread is a no no."
let userEnteredText2 = "This fine didelo thread is a go."
print(containsSwearWord(userEnteredText1, swearWords: listOfSwearWords)) // true
print(containsSwearWord(userEnteredText2, swearWords: listOfSwearWords)) // false
I created a class that enables you to feed a string in and remove profanity. Here's a link to the repo.
Here's the code:
class ProfanityFilter: NSObject {
static let sharedInstance = ProfanityFilter()
private override init() {}
// Customize as needed
private let dirtyWords = "\\b(ducker|mother ducker|motherducker|shot|bad word|another bad word|)\\b"
// Courtesy of Martin R
// https://stackoverflow.com/users/1187415/martin-r
private func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [.caseInsensitive])
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
public func cleanUp(_ string: String) -> String {
let dirtyWords = matches(for: self.dirtyWords, in: string)
if dirtyWords.count == 0 {
return string
} else {
var newString = string
dirtyWords.forEach({ dirtyWord in
let newWord = String(repeating: "😲", count: dirtyWord.characters.count)
newString = newString.replacingOccurrences(of: dirtyWord, with: newWord, options: [.caseInsensitive])
})
return newString
}
}
}
Usage:
yourLabel.text = ProfanityFilter.sharedInstance.cleanUp(yourString)
Extension checking for foul language.
Swift 4.2
Example Usage:
"poop".containsBadWord()
Extension:
extension String {
func containsBadWord()->Bool {
//Sorry for bad words
let badWords = ["insert","bad","words","here","poop"]
for word in badWords {
if lowercased().contains(word) {
return true
}
}
return false
}
}
I would suggest looking into an API to which you can submit a string and get a JSON response containing information such as:
Is the string bad?
Total # of bad words contained in string
An array containing all recognized bad words
A censored version of the input string
I found a couple sources via Google. Check these out and do a little more research to find if an API is the best fit for you and which one you should use. I would assume that using an API like the one I have listed below would be the most practical approach, as you would NOT have to compile a list of "bad" words yourself and use resources from the device to sort through the list (which can contain thousands of words).
Rather, you can simply submit a string using the API to get a network response containing the data in JSON format from the API server.
Why not let the API Server do the logic for you and just spit out an answer?
NeutrinoAPI
If this method returns a range,
str.range(of: "darn|crap|newb", options: [.regularExpressionSearch, .caseInsensitiveSearch], range: str.startIndex..<str.endIndex, locale:nil)
an offensive word has been found. While this method can be used to remove the offending strings:
str.replacingOccurrences(of: "darn|crap|newb", with: "", options: [.regularExpressionSearch, .caseInsensitiveSearch])
If you filter bad words locally you wouldn’t easily accommodate many languages, also as new bad words appear, you would have to waste a developers time manually putting in bad words, alternatively, there are apis designed for this purpose: https://www.moderatecontent.com/documentation/badwords

Swift filter array of strings

I've had troubles filtering array of keywords (strings) in swift ,My code:
self.filteredKeywords=filter(keywords.allValues, {(keyword:NSString) ->
Bool in
let words=keyword as? NSString
return words?.containsString(searchText)
})
As AnyObject can't be subtype of NSString, I'm stuck with this!
[Updated for Swift 2.0]
As NSString is toll-free bridged to Swift String, just avoid the coercions with:
3> ["abc", "bcd", "xyz"].filter() { nil != $0.rangeOfString("bc") }
$R1: [String] = 2 values {
[0] = "abc"
[1] = "bcd"
}
But, if you think allValues aren't strings:
(keywords.allValues as? [String]).filter() { nil != $0.rangeOfString("bc") }
which returns an optional array.
Your filter is over [AnyObject], but your closure takes NSString. These need to match. Also, your result needs to be a Bool, not a Bool?. You can address these simply like this:
self.filteredKeywords = filter(keywords.allValues, {
let keyword = $0 as? NSString
return keyword?.containsString(searchText) ?? false
})
This accepts AnyObject and then tries to coerce it down to NSString. It then nil-coalleces (??) the result to make sure it always is a Bool.
I'd recommend, though, treating keywords as a [String:String] rather than an NSDictionary. That would get rid of all the complications of AnyObject. Then you can just do this:
self.filteredKeywords = keywords.values.filter { $0.rangeOfString(searchText) != nil }
Whenever possible, convert Foundation collections into Swift collections as soon as you can and store those. If you have incoming Foundation objects, you can generally convert them easily with techniques like:
let dict = nsdict as? [String:String] ?? [:]
Or you can do the following to convert them such that they'll crash in debug (but silently "work" in release):
func failWith<T>(msg: String, value: T) -> T {
assertionFailure(msg)
return value
}
let dict = nsdict as? [String:String] ?? failWith("Couldn't convert \(d)", [:])
Swift 4.2 provides a new way to do this:
var theBigLebowski = ["The Dude", "Angry Walter", "Maude Lebowski", "Donny Kerabatsos", "The Big Lebowski", "Little Larry Sellers"]
// after removeAll -> ["The Dude", "Angry Walter", "Donny Kerabatsos", "Little Larry Sellers"]
theBigLebowski.removeAll{ $0.contains("Lebowski")}
print(theBigLebowski)
There is both a problem with GoZoner's answer for certain data types and also a slightly better way to do this. The following examples can show this:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Dog, Cat, Otter, Deer, Rabbit]
Likely not the set you expected!
However this works fine this way if we don't type animalArray as an NSMutableArray:
let animalArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]
However I'd recommend using $0.contains() instead of $0.rangeOfString() != nil because it functions in both circumstances and slightly enhances the readability of the code:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.contains("er") }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]

Random unique string generation to use as nonce (oauth)

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)")

Resources