I am using multiline string as follows. there is a line where I display submittedPerson, either can be his Id or email, but also can be nil as well. I wonder how do you hide this line if it returns nil
var submittedPerson = ""
if let Id = User[index].Id {
submittedPerson = Id
} else if let email = User[index].email {
submittedPerson = email
}
let displayStr = """
\(department)
\"submittedBy" : \(submittedPerson)
\(submittedDate)
"""
I'll assume the hidden requirement here is that you still want to keep the multiline string literal readable, and not have code duplication :)
One way you could do this is to move one of the two line feed characters to when you assign to submittedPerson:
var submittedPerson: String? = ""
if let Id = User[index].Id {
submittedPerson = "\n\(Id)\n" // note the lines feeds
} else if let email = User[index].email {
submittedPerson = "\n\(email)\n"
}
let displayStr = """
\(department)
\(submittedPerson ?? "")
\(submittedDate)
"""
Related
I'm parsing a text file to get the latitude and longitude of locations. I need to convert the lon/lat strings to doubles, but I can't do it.
I've tried both the Double(String) method and the (String as NSNumber).doubleValue. It always gives nil.
When I type in the numbers manually it works.
Here's the code snippet:
var items = [[String]]()
func readParkingData() {
guard let filepath = Bundle.main.path(forResource: "parking", ofType: "txt") else {
print("file not found")
return
}
print("file path : \(filepath)")
do{
let content = try String(contentsOfFile: filepath, encoding: .utf8)
let attributed = content.htmlAttributedString
let decoded : String = attributed!.string
let split = decoded.split(separator: ";")
var count = 0
var item = [String]()
for word in split {
item.append(String(word))
count += 1
if count == 30 {
items.append(item)
item = [String]()
count = 0
}
}
for entry in items {
print(entry[24])
print(entry[25])
let latString : String = entry[24]
let lonString : String = entry[25]
print(type(of: latString))
let lat = Double(latString)
print(lat)
}
}catch{
print("file read error \(filepath)")
}
}
I've looked through the other answers. The type of latString is String, not optional. Trimming white spaces didn't help either. lat is always nil.
What's going on here?
Apparently the floating point numbers are enclosed in quotation marks,
so you'll need not only trim whitespace but also quotation marks. Example:
let latString = "\"12.34\""
print(latString) // "12.34"
var cs = CharacterSet.whitespaces
cs.insert("\"")
let trimmedLatString = latString.trimmingCharacters(in: cs)
print(trimmedLatString) // 12.34
print(Double(trimmedLatString)!) // 12.34
Further remarks:
I do not see the reason to operate on the htmlAttributedString, you
probably should split the original content into lines and fields.
Is your input a CSV-formatted file? There are open source CSV reader libraries
which you might try.
I am consuming an API that gives me the next page in the Header inside a field called Link. (For example Github does the same, so it isn't weird.Github Doc)
The service that I am consuming retrieve me the pagination data in the following way:
As we can see in the "Link" gives me the next page,
With $0.response?.allHeaderFields["Link"]: I get </api/games?page=1&size=20>; rel="next",</api/games?page=25&size=20>; rel="last",</api/games?page=0&size=20>; rel="first".
I have found the following code to read the page, but it is very dirty... And I would like if anyone has dealt with the same problem or if there is a standard way of face with it. (I have also searched if alamofire supports any kind of feature for this but I haven't found it)
// MARK: - Pagination
private func getNextPageFromHeaders(response: NSHTTPURLResponse?) -> String? {
if let linkHeader = response?.allHeaderFields["Link"] as? String {
/* looks like:
<https://api.github.com/user/20267/gists?page=2>; rel="next", <https://api.github.com/user/20267/gists?page=6>; rel="last"
*/
// so split on "," the on ";"
let components = linkHeader.characters.split {$0 == ","}.map { String($0) }
// now we have 2 lines like '<https://api.github.com/user/20267/gists?page=2>; rel="next"'
// So let's get the URL out of there:
for item in components {
// see if it's "next"
let rangeOfNext = item.rangeOfString("rel=\"next\"", options: [])
if rangeOfNext != nil {
let rangeOfPaddedURL = item.rangeOfString("<(.*)>;", options: .RegularExpressionSearch)
if let range = rangeOfPaddedURL {
let nextURL = item.substringWithRange(range)
// strip off the < and >;
let startIndex = nextURL.startIndex.advancedBy(1) //advance as much as you like
let endIndex = nextURL.endIndex.advancedBy(-2)
let urlRange = startIndex..<endIndex
return nextURL.substringWithRange(urlRange)
}
}
}
}
return nil
}
I think that the forEach() could have a better solution, but here is what I got:
let linkHeader = "</api/games?page=1&size=20>; rel=\"next\",</api/games?page=25&size=20>; rel=\"last\",</api/games?page=0&size=20>; rel=\"first\""
let links = linkHeader.components(separatedBy: ",")
var dictionary: [String: String] = [:]
links.forEach({
let components = $0.components(separatedBy:"; ")
let cleanPath = components[0].trimmingCharacters(in: CharacterSet(charactersIn: "<>"))
dictionary[components[1]] = cleanPath
})
if let nextPagePath = dictionary["rel=\"next\""] {
print("nextPagePath: \(nextPagePath)")
}
//Bonus
if let lastPagePath = dictionary["rel=\"last\""] {
print("lastPagePath: \(lastPagePath)")
}
if let firstPagePath = dictionary["rel=\"first\""] {
print("firstPagePath: \(firstPagePath)")
}
Console output:
$> nextPagePath: /api/games?page=1&size=20
$> lastPagePath: /api/games?page=25&size=20
$> firstPagePath: /api/games?page=0&size=20
I used components(separatedBy:) instead of split() to avoid the String() conversion at the end.
I created a Dictionary for the values to hold and removed the < and > with a trim.
In my app I save the user's phone numbers they give me so others can find them and they can find their contacts on the app. But I am having trouble parsing through the phone numbers because users can save phone numbers in different ways:
– Country code, area code, number: +1 (area code) xxx-xxxx
– Country code, number: +1 xxx-xxxx
– Number: xxx-xxxx
The way I save the users number is by adding the area code and number as one long number and save the country code separately. I tried parsing through the telephone numbers taken from Contacts to mimic how I saved the numbers but it does not work. Is there something I am missing? I have copied the code at the bottom:
if accessGranted {
let contactStore = CNContactStore()
let keys = [CNContactPhoneNumbersKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
try contactStore.enumerateContacts(with: request, usingBlock: { (contact, error) in
let phoneNumberRaw = contact.phoneNumbers.first?.value.stringValue ?? ""
var phoneNumberRawArray = phoneNumberRaw.components(separatedBy: " ")
var phoneString = ""
if phoneNumberRawArray.count >= 3 { //probably has country code in the front
//remove country code
phoneNumberRawArray.removeFirst()
}
//add to phone string
for phone in phoneNumberRawArray {
phoneString.append(phone)
}
phoneString = phoneString.replacingOccurrences(of: "(", with: "")
phoneString = phoneString.replacingOccurrences(of: " ", with: "")
phoneString = phoneString.replacingOccurrences(of: ")", with: "")
phoneString = phoneString.replacingOccurrences(of: "-", with: "")
self.contactsPhoneNumbers.append(phoneString)
//get country code
for ContactNumber:CNLabeledValue in contact.phoneNumbers
{
let fullNumber = ContactNumber.value
let countryCode = fullNumber.value(forKey: "countryCode") as? String
self.contactsCountryCode.append(countryCode!)
}
})
}
catch {
print("Unable to get contacts")
}
}
I want to get only first letter from last name for privacy of users. Example: "John D."
extension String
{
public func getAcronyms(separator: String = "") -> String
{
let acronyms = self.components(separatedBy: " ").map({ String($0.characters.first!) }).joined(separator: separator);
return acronyms;
}
}
For proper naming, you have to use PersonNameComponentsFormatter.
let name = "Joe Singh"
let nameFormatter = PersonNameComponentsFormatter()
if let nameComps = nameFormatter.personNameComponents(from: name), let firstLetter = nameComps.givenName?.first, let lastName = nameComps.familyName {
let sortName = "\(firstLetter). \(lastName)" // J. Singh
}
You can also find:
nameComps.middleName
nameComps.familyName
nameComps.nameSuffix
nameComps.namePrefix
And also can configured the format of your names
Default
short
long
abbreviated
I'm getting a value like this 264.8 and I want to take the value before and after the dot. I can take the value before the dot like this
var string = "264.8"
var index = string2.rangeOfString(".", options: .BackwardsSearch)?.startIndex
var substring = string.substringToIndex(index2!)
but please how I can take it after the dot?
Try this code:
var string = "264.8"
var numbers = string.componentsSeparatedByString(".")
print(numbers[0])
print(numbers[1])
var string = "264.8"
let partsArr = string.componentsSeparatedByString(".")
var beforeDot: String = partsArr[0]
var afterDot: String? = partsArr[1]
Just for the sake of completeness, an alternative is to use split:
let string = "264.8"
let result = string.characters.split(".").map { String($0) }
print(result[0]) // "264"
print(result[1]) // "8"
And another one is to use componentsSeparatedByCharactersInSet:
let string = "264.8"
let result = string.componentsSeparatedByCharactersInSet(NSCharacterSet.punctuationCharacterSet())
print(result[0]) // "264"
print(result[1]) // "8"
Alternatively, define a closure variable that handles the conversion for you
let mySubstringClosure : (String) -> (String) = { $0.componentsSeparatedByString(".").first ?? $0 }
let substring1 = mySubstringClosure("264.8") // "264"
let substring2 = mySubstringClosure("264") // "264"
let substring3 = mySubstringClosure("") // ""
Note that this code runs safely even if no dot . exists in the string, or of the string is empty.