Get rid of these Optional values - string

Using Xcode 7 beta, Swift 2.0
I'm saving and loading credentials to keychain, somehow when loading I get "Optional(value)" back, it looks like this is really part of the string as it also displayed like so in a textbox or when sending to API
This is how I save and load credentials now, as you see I've done a lot of extra nil checking to make sure it is not nil or Optional, it is indeed a overuse of explanation marks...
func SaveCredentials(credentials : [String : String!]!) -> Bool
{
if(credentials.count == 2)
{
//only proceed when we have two keys: username and password
let username = credentials["username"]
let password = credentials["password"]
if let usernameStr = username
{//also tried username!=nil && password != nil
if let passwordStr = password
{ //usernameStr and passwordStr is of type String!
let NsDataUsername = usernameStr!.dataUsingEncoding(NSUTF8StringEncoding)
let NsDataPassword = passwordStr!.dataUsingEncoding(NSUTF8StringEncoding)
if(NsDataUsername != nil && NsDataPassword != nil)
{
LocalStorage.saveToKeyChain("username", data: NsDataUsername!)
LocalStorage.saveToKeyChain("password", data: NsDataPassword!)
return true
}
}
}
}
return false
}
func LoadCredentials() -> [String : String!]?
{
let NsDataUsername = LocalStorage.loadFromKeyChain("username")
let NsDataPassword = LocalStorage.loadFromKeyChain("password")
if(NsDataUsername != nil && NsDataPassword != nil)
{
let username : String! = String(NSString(data: NsDataUsername!, encoding: NSUTF8StringEncoding))
let password : String! = String(NSString(data: NsDataPassword!, encoding: NSUTF8StringEncoding))
if let usernameStr = username
{
if let passwordStr = password
{ // password is of type String!, passwordStr is of type String
var credentials : [String: String!] = [String : String]()
credentials["username"] = usernameStr
credentials["password"] = passwordStr
return credentials
}
}
}
return nil
}
And when I send to Api, this is my method that also requires a non-optional string. This method does work when logging in, getting strings from text fields, but does not filter out that Optional when coming from keychain.
func LoginUser(email : String!, password : String!)
{
print("LoginUser(email : \(email), password: \(password))")
var parameters = [String : AnyObject]()
parameters["UserName"] = email
parameters["Password"] = password
......
The strings that I send to the SaveCredentials method, are the same that the user logged in with:
func LoginLocalAccount(email : String!, password : String!)
{
databaseAPI.LoginUser(email!, password: password!) //login goes just fine
saveCredentials(email!, password: password!) //manages to get Optional in it..
}
I suspect it has something to do with saving and loading from keychain, for interests, this is what I use to save and load from keychain.
I want to get rid of them because when the app starts, it loads the credentials and tries to login at my API. Ofcourse I get an error back that the username is not a valid e-mail, because it is Optional(email#adress.com)

You're overusing !. You don't need them. Try to learn more about implicitly unwrapped optionals, optionals, ... Your code is a mess (no offense, everybody's learning).
Back to your optional problem, it's caused by this line:
let username : String! = String(NSString(data: NsDataUsername!, encoding: NSUTF8StringEncoding))
convenience init?(data: NSData, encoding: UInt) - inner part utilizes failable initializer, so, NSString? is the result. Then initialization of String with optional NSString? produces optional as well. But, it has no sense at all do it in this way.
First part - remove optional
Utilizing new guard:
guard let loadedPassword = NSString(data: passwordData, encoding: NSUTF8StringEncoding) else {
fatalError("Ooops")
}
loadedPassword contains NSString (not NSString?) now.
Second part - NSString -> String
You did probably read (if not, read) Strings and Characters about bridging, ... If you can freely exchange NSString with String, you can think that you're done:
var dict = [String:String]()
dict["password"] = loadedPassword
Nope. It produces following error:
NSString is not implicitly convertible to String; did you mean to
use 'as' to explicitly convert?
Slight change and now you're done:
var dict = [String:String]()
dict["password"] = loadedPassword as String
Complete example
let password = "Hallo"
guard let passwordData = password.dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("Ooops")
}
// save/load to/from keychain
guard let loadedPassword = NSString(data: passwordData, encoding: NSUTF8StringEncoding) else {
fatalError("Ooops")
}
var dict = [String:String]()
dict["password"] = loadedPassword as String
print(dict) // "[password: Hallo]\n"

Related

Terraform, get value from map

Prompt me, please, how can I get separately a value for key paswd-0. I mean, I need separated values for password and username.
This is remote data from data.terraform_remote_state.user_passwd.outputs.login_passwd
output = {
paswd-0 = jsonencode(
{
password = "uGo="
username = "git"
}
)
paswd-1 = jsonencode(
{
password = "wM="
username = "kun"
}
)
}
I'm trying this and get error parameter: lookup() requires a map as the
output "tetts" {
value = lookup(tomap(data.terraform_remote_state.user_passwd.outputs.login_passwd.paswd-0), "password", null)
}
Ideally I would go through of each value and fill these fields.
argocd_repositories = {
[
"private-repo" = {
url = "https://repo.git"
username = "argocd"
password = "access_token"
},
"git-repo" = {
url = "https://repo.git"
password = "argocd_access_token"
username = "admin"
},
"private-helm-chart" = {
url = "https://charts.jetstack.io"
type = "helm"
username = "foo"
password = "bar"
},
]
}
As per my comment, you can get the value from the data source by using the jsondecode built-in function [1]. You would have to update the output to look like the following:
output "tetts" {
value = lookup(tomap(jsondecode(data.terraform_remote_state.user_passwd.outputs.login_passwd["paswd-0"]), "password", null)
}
This is only to make it work as you intended it to. However, it will output only the value for the password. Since I do not have the remote state, I managed to get close to what you want with locals and the following:
locals {
output = {
paswd-0 = jsonencode(
{
password = "uGo="
username = "git"
}
)
paswd-1 = jsonencode(
{
password = "wM="
username = "kun"
}
)
}
sorted_values = { for k, v in local.output : jsondecode(v).username => jsondecode(v).password }
}
Note that jsondecode is used on the values of the original map. Furthermore, since the JSON decoded values are also in a key value pair format, you can access the keys and corresponding values using the usual terraform notation (i.e., jsondecode(v).username and jsondecode(v).password). Using terraform console, the local sorted_values variable has the following look:
> local.sorted_values
{
"git" = "uGo="
"kun" = "wM="
}
I guess this is close to what you wanted to achieve with the tomap function.
[1] https://www.terraform.io/language/functions/jsondecode

Swift : session sendData , type () does not confirm to protocol BooleanType

I am trying to create a Bluetooth chat system based on this tutorial : http://www.appcoda.com/chat-app-swift-tutorial/
The error that I receive is the following : type () does not conform to protocol BooleanType.
Does anyone know how to solve this?
func sendData(dictionaryWithData dictionary: Dictionary<String, String>, toPeer targetPeer: [MCPeerID]){
let dataToSend = NSKeyedArchiver.archivedDataWithRootObject(dictionary)
//let peersArray = NSArray(object: targetPeer)
if session.sendData(dataToSend, toPeers: targetPeer , withMode: MCSessionSendDataMode.Reliable) {
}
}
Well the error states that you use a var as a bool, while it is not a bool, my advice, stop using non type specific inits.
example:
var myBool = true //BAD
var mySecondBool : Bool = true //Good
The thing is I see no bools in the code you posted...

Swift UTF16 Substring

I'm receiving a string from the server in the following format:
118|...message...215|...message2...
Basically, it's the message length followed by a pipe and the message itself, repeated for multiple messages. The message is encoded UTF16.
I'm looking for a way to parse this in Swift. I know I could cast this as NSString and use standard indexes/ranges on that because UTF16 is what NSString uses, but I'm wondering what is the Swift way to handle this? I can't seem to find a way to pull a substring out of a String based on a UTF16 encoding.
Update
I'm not trying to initialize a String with raw UTF16 Data (there's plenty of ways to do that). I already have the string, so I'm trying to take a String in the above format and parse it. The issue I have is that the message length given to me by the server is based on UTF16. I can't simply extract the length and call String.advance(messageLength) on the Index because the length I've been given doesn't match the grapheme clusters that Swift advances on. My issue is that I can't extract from the string the message in Swift. I have to instead cast it over to NSString and then use "normal" NSRange on it. My question is how do I pull the substring out by extracting a range based on my search for the first pipe, and then use the length provided by the parser in UTF16.
This is all extremely simple to do with NSString. Not sure how it can be done in pure Swift (or if it can be done).
Here is my take on parsing the messages out of the string. I had to change your lengths to work with the string.
let message = "13|...message...14|...message2..."
let utf16 = message.utf16
var startingIndex = message.utf16.startIndex
var travellingIndex = message.utf16.startIndex
var messages = [String]()
var messageLength: Int
while travellingIndex != message.utf16.endIndex {
// Start walking through each character
if let char = String(utf16[travellingIndex..<travellingIndex.successor()]) {
// When we find the pipe symbol try to parse out the message length
if char == "|" {
if let stringNumber = Int(String(utf16[startingIndex..<travellingIndex])) {
messageLength = stringNumber
// We found the lenght, now skip the pipe character
startingIndex = travellingIndex.successor()
// move the travelingIndex to the end of the message
travellingIndex = travellingIndex.advancedBy(messageLength)
// get the message and put it into an array
if let message = String(utf16[startingIndex...travellingIndex]) {
messages.append(message)
startingIndex = travellingIndex.successor()
}
}
}
}
travellingIndex = travellingIndex.successor()
}
print(messages)
The output I get at the end is:
["...message...", "...message2..."]
The Foundation framework extends String to be initialisable from data:
import Foundation
let string = String(data: data, encoding: NSUTF16StringEncoding)
Getting rid of Foundation is not possible unless you implement the decoding yourself. Note that with Swift going open-source, Foundation is getting reimplemented without Objective-C dependency here.
EDIT: Thanks, Martin R, the link you provided is indeed working in pure Swift :D
EDIT:
There is the utf16 property of a String whose count property is the length in UTF16. Here is a simple parser for your purpose, efficiency isn't great, but it gets the job done:
func getMessages(var string: String) -> [String]? {
func getMessage(string: String) -> (message: String, rest: String)? {
guard let
index = string.characters.indexOf("|"),
length = Int(String(string.characters.prefixUpTo(index)))
else { return nil }
let msgRest = String(string.characters.suffixFrom(index.successor()))
return (String(msgRest.utf16.prefix(length)), String(msgRest.utf16.dropFirst(length)))
}
var messages : [String] = []
while let (message, rest) = getMessage(string) {
string = rest
messages.append(message)
}
return messages
}
func stringForMessages(messages: [String]) -> String {
return messages.map{ "\($0.utf16.count)|\($0)" }.joinWithSeparator("")
}
let messages = [
"123",
"πŸ’†πŸ½πŸ’†πŸ½πŸ’†πŸ½",
"πŸ™‰πŸ˜‡πŸŽ…πŸΏ",
"6πŸ•΄βš½οΈ"
]
let string = stringForMessages(messages)
let received = getMessages(string)
messages // ["123", "πŸ’†πŸ½πŸ’†πŸ½πŸ’†πŸ½", "πŸ™‰πŸ˜‡πŸŽ…πŸΏ", "6πŸ•΄βš½οΈ"]
I actually tried making it more efficient, but Swift's String mechanics pushed against it.. I challenge anyone to create a beautiful efficient crash-safe parser for this..

How to use stringByAddingPercentEncodingWithAllowedCharacters() for a URL in Swift 2.0

I was using this, in Swift 1.2
let urlwithPercentEscapes = myurlstring.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
This now gives me a warning asking me to use
stringByAddingPercentEncodingWithAllowedCharacters
I need to use a NSCharacterSet as an argument, but there are so many and I cannot determine what one will give me the same outcome as the previously used method.
An example URL I want to use will be like this
http://www.mapquestapi.com/geocoding/v1/batch?key=YOUR_KEY_HERE&callback=renderBatch&location=Pottsville,PA&location=Red Lion&location=19036&location=1090 N Charlotte St, Lancaster, PA
The URL Character Set for encoding seems to contain sets the trim my
URL. i.e,
The path component of a URL is the component immediately following the
host component (if present). It ends wherever the query or fragment
component begins. For example, in the URL
http://www.example.com/index.php?key1=value1, the path component is
/index.php.
However I don't want to trim any aspect of it.
When I used my String, for example myurlstring it would fail.
But when used the following, then there were no issues. It encoded the string with some magic and I could get my URL data.
let urlwithPercentEscapes = myurlstring.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
As it
Returns a representation of the String using a given encoding to
determine the percent escapes necessary to convert the String into a
legal URL string
Thanks
For the given URL string the equivalent to
let urlwithPercentEscapes = myurlstring.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
is the character set URLQueryAllowedCharacterSet
let urlwithPercentEscapes = myurlstring.stringByAddingPercentEncodingWithAllowedCharacters( NSCharacterSet.URLQueryAllowedCharacterSet())
Swift 3:
let urlwithPercentEscapes = myurlstring.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
It encodes everything after the question mark in the URL string.
Since the method stringByAddingPercentEncodingWithAllowedCharacters can return nil, use optional bindings as suggested in the answer of Leo Dabus.
It will depend on your url. If your url is a path you can use the character set
urlPathAllowed
let myFileString = "My File.txt"
if let urlwithPercentEscapes = myFileString.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
print(urlwithPercentEscapes) // "My%20File.txt"
}
Creating a Character Set for URL Encoding
urlFragmentAllowed
urlHostAllowed
urlPasswordAllowed
urlQueryAllowed
urlUserAllowed
You can create also your own url character set:
let myUrlString = "http://www.mapquestapi.com/geocoding/v1/batch?key=YOUR_KEY_HERE&callback=renderBatch&location=Pottsville,PA&location=Red Lion&location=19036&location=1090 N Charlotte St, Lancaster, PA"
let urlSet = CharacterSet.urlFragmentAllowed
.union(.urlHostAllowed)
.union(.urlPasswordAllowed)
.union(.urlQueryAllowed)
.union(.urlUserAllowed)
extension CharacterSet {
static let urlAllowed = CharacterSet.urlFragmentAllowed
.union(.urlHostAllowed)
.union(.urlPasswordAllowed)
.union(.urlQueryAllowed)
.union(.urlUserAllowed)
}
if let urlwithPercentEscapes = myUrlString.addingPercentEncoding(withAllowedCharacters: .urlAllowed) {
print(urlwithPercentEscapes) // "http://www.mapquestapi.com/geocoding/v1/batch?key=YOUR_KEY_HERE&callback=renderBatch&location=Pottsville,PA&location=Red%20Lion&location=19036&location=1090%20N%20Charlotte%20St,%20Lancaster,%20PA"
}
Another option is to use URLComponents to properly create your url
Swift 3.0 (From grokswift)
Creating URLs from strings is a minefield for bugs. Just miss a single / or accidentally URL encode the ? in a query and your API call will fail and your app won’t have any data to display (or even crash if you didn’t anticipate that possibility). Since iOS 8 there’s a better way to build URLs using NSURLComponents and NSURLQueryItems.
func createURLWithComponents() -> URL? {
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "www.mapquestapi.com"
urlComponents.path = "/geocoding/v1/batch"
let key = URLQueryItem(name: "key", value: "YOUR_KEY_HERE")
let callback = URLQueryItem(name: "callback", value: "renderBatch")
let locationA = URLQueryItem(name: "location", value: "Pottsville,PA")
let locationB = URLQueryItem(name: "location", value: "Red Lion")
let locationC = URLQueryItem(name: "location", value: "19036")
let locationD = URLQueryItem(name: "location", value: "1090 N Charlotte St, Lancaster, PA")
urlComponents.queryItems = [key, callback, locationA, locationB, locationC, locationD]
return urlComponents.url
}
Below is the code to access url using guard statement.
guard let url = createURLWithComponents() else {
print("invalid URL")
return nil
}
print(url)
Output:
http://www.mapquestapi.com/geocoding/v1/batch?key=YOUR_KEY_HERE&callback=renderBatch&location=Pottsville,PA&location=Red%20Lion&location=19036&location=1090%20N%20Charlotte%20St,%20Lancaster,%20PA
In Swift 3.1, I am using something like the following:
let query = "param1=value1&param2=" + valueToEncode.addingPercentEncoding(withAllowedCharacters: .alphanumeric)
It's safer than .urlQueryAllowed and the others, because it this will encode every characters other than A-Z, a-z and 0-9. This works better when the value you are encoding may use special characters like ?, &, =, + and spaces.
In my case where the last component was non latin characters I did the following in Swift 2.2:
extension String {
func encodeUTF8() -> String? {
//If I can create an NSURL out of the string nothing is wrong with it
if let _ = NSURL(string: self) {
return self
}
//Get the last component from the string this will return subSequence
let optionalLastComponent = self.characters.split { $0 == "/" }.last
if let lastComponent = optionalLastComponent {
//Get the string from the sub sequence by mapping the characters to [String] then reduce the array to String
let lastComponentAsString = lastComponent.map { String($0) }.reduce("", combine: +)
//Get the range of the last component
if let rangeOfLastComponent = self.rangeOfString(lastComponentAsString) {
//Get the string without its last component
let stringWithoutLastComponent = self.substringToIndex(rangeOfLastComponent.startIndex)
//Encode the last component
if let lastComponentEncoded = lastComponentAsString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet()) {
//Finally append the original string (without its last component) to the encoded part (encoded last component)
let encodedString = stringWithoutLastComponent + lastComponentEncoded
//Return the string (original string/encoded string)
return encodedString
}
}
}
return nil;
}
}
Swift 4.0
let encodedData = myUrlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed)

How can i replace a specific word in a string in swift?

I am looking for a way to replace a word inside a string in swift. Can anyone help?
this is what I have so far, I can find the specific word, but i do not know how to replace it...
var str = "helo, playgound"
var findWords = ["helo","playgound"]
var replaceWords = ["hello","playground"]
extension String {
var wordList:[String] {
return "".join(componentsSeparatedByCharactersInSet(NSCharacterSet.punctuationCharacterSet())).componentsSeparatedByString(" ")
}
}
func stringToArray() -> Array<String> {
var arr = str.wordList
return arr
}
func correction(var _arr:Array<String>) -> String{
for var i = 0; i < _arr.count; i++ {
if str.lowercaseString.rangeOfString(findWords[i]) != nil {
println("exists")
}
}
return str
}
It depends what your definition of a "word" is. If you're looking for an intelligent built-in notion of a "word", the easiest solution is probably to use NSRegularExpression, which knows where "word" boundaries are:
var s = NSMutableString(string:"hello world, go to hell")
let r = NSRegularExpression(
pattern: "\\bhell\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: "heaven")
After that, s is "hello world, go to heaven", which is the right answer; we replaced the "hell" that is a word, but not the "hell" in "hello". Notice that we are also matching case-insensitively, which seems to be one of your desiderata.
That example shows how do just one pair ("hell" and "heaven") but it is easy to abstract it into a method so that you can do it again and again for further pairs:
var str = "helo, playgound"
var findWords = ["helo", "playgound"]
var replaceWords = ["hello", "playground"]
func correct(str:String, orig:String, repl:String) -> String {
var s = NSMutableString(string:str)
let r = NSRegularExpression(
pattern: "\\b\(orig)\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: repl)
return s
}
for pair in Zip2(findWords,replaceWords) {
str = correct(str, pair.0, pair.1)
}
str // hello, playground
The easiest is probably this:
let statement = "Swift is hard."
let swiftRange = statement.startIndex..<advance(statement.startIndex, 5)
let newStatement = statement.stringByReplacingCharactersInRange(swiftRange, withString: "Objective-C")
// now newStatement = "Objective-C is hard."
Following a longer commenting tour: The above is under the assumption of the OP "I can find the specific word, but i do not know how to replace it...", so it's not about finding a "word" which to define is another discussion. It's just about replacing an already found word.
Another word on stringByReplacingCharactersInRange: #matt states that this is Cocoa cross-over. In that case Apple is telling a plain lie:
I fostered the web but there's no Apple source telling anything. Only the Foundation method for NSString. Their Swift book is silent too (in many respects). Well, I don't trust Apple anyway any longer since Yosemite-fail.

Resources