Extracting Email Domain in Swift/Myplayground - swift

I'm compartmentalizing users on firebase according to their email domain. What universal code can I use to extract only 'havard' in the following examples? All user emails end in '.edu'
let email = jsmith#student.havard.edu ,
let email = jsmith#havard.edu

Here is a simple function that will work for the input set you provided.
func getMainPart(s: String) -> String {
let charSet = NSCharacterSet(charactersInString: ".#")
let v = s.componentsSeparatedByCharactersInSet(charSet)
let pos = v.count - 2
return v[pos]
}
Here's another solution:
func getMainPart2(s: String) -> String {
var v = s.componentsSeparatedByString("#").last?.componentsSeparatedByString(".")
v?.removeLast()
return (v!.last)!
}
You can call it like this:
let email1 = "smith#harvard.edu"
let s = getMainPart2(email1)
print(s) // this outputs: harvard

One legal email address has only one "#" so separate email address:
public func componentsSeparatedByString(separator: String) -> [String]
This is sample code:
let components = email.componentsSeparatedByString("#").last?.componentsSeparatedByString(".")
if let index = components?.endIndex.advancedBy(-2) {
components![index]
}

Related

Email validation in swift

I found this code in google:
func isEmail(email: String) -> Bool {
let emailRegx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailTest = NSPredicate(format: "SELF MATCHES %#", emailRegx)
return emailTest.evaluate(with: email)
}
but I want to change the emailRegx to ensure that the first two characters in an email address can not be a number or symbols
Your question is not very specific and lacks details or some code that you have tried.
However, this is my take based on how I understood your issue:
import Foundation
var validMail = "foo#bar.com"
var invalidMail = "1foo#bar.com"
func validateFirstLetter(_ string: String) -> Bool {
string.first?.isLetter == true
}
print( validateFirstLetter(validMail) ) // true
print( validateFirstLetter(invalidMail) ) // false
For proper email validation you will find many implementations with a simple google search. For example:
How to validate an e-mail address in swift?

Parse String into an object in Swift

I have received this response from the server and I am sure there must be a more efficient way to convert it into an object.
I have the following response:
[
id=2997,rapidViewId=62,state=ACTIVE,name=Sprint7,startDate=2018-11-20T10:28:37.256Z,endDate=2018-11-30T10:28:00.000Z,completeDate=<null>,sequence=2992,goal=none
]
How do I convert it nicely into a well formed swift object in the simplest way?
Here is my attempt which gives me just the Sprint Value
if sprintJiraCustomField.count > 0 {
let stringOutput = sprintJiraCustomField.first?.stringValue // convert output to String
let name = stringOutput?.components(separatedBy: "name=") // get name section from string
let nameFieldRaw = name![1].components(separatedBy: ",") // split out to the comma
let nameValue = nameFieldRaw.first!
sprintDetail = nameValue// show name field
}
Not sure what format you want but the below code will produce an array of tuples (key, value) but all values are strings so I guess another conversion is needed afterwards
let items = stringOutput.components(separatedBy: ",").compactMap( {pair -> (String, String) in
let keyValue = pair.components(separatedBy: "=")
return (keyValue[0], keyValue[1])
})
This is a work for reduce:
let keyValueStrings = yourString.components(separatedBy: ",")
let dictionary = keyValueStrings.reduce([String: String]()) {
(var aggregate: [String: String], element: String) -> [String: String] in
let elements = element.componentsSeparatedByString("=")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
aggregate[key] = value
return aggregate
}
This is a functional approach, but you can achieve the same using a for iteration.
So then you can use Swift’s basic way of mapping. for example you will have your custom object struct. First, you will add an init method to it. Then map your object like this:
init(with dictionary: [String: Any]?) {
guard let dictionary = dictionary else { return }
attribute = dictionary["attrName"] as? String
}
let customObjec = CustomStruct(dictionary: dictionary)
We already have some suggestion to first split the string at each comma and then split each part at the equals sign. This is rather easy to code and works well, but it is not very efficient as every character has to be checked multiple times. Writing a proper parser using Scanner is just as easy, but will run faster.
Basically the scanner can check if a given string is at the current position or give you the substring up to the next occurrence of a separator.
With that the algorithm would have the following steps:
Create scanner with the input string
Check for the opening bracket, otherwise fail
Scan up to the first =. This is the key
Consume the =
Scan up to the first , or ]. This is the value
Store the key/value pair
If there is a , consume it and continue with step 3
Consume the final ].
Sadly the Scanner API is not very Swift-friendly. With a small extension it is much easier to use:
extension Scanner {
func scanString(_ string: String) -> Bool {
return scanString(string, into: nil)
}
func scanUpTo(_ delimiter: String) -> String? {
var result: NSString? = nil
guard scanUpTo(delimiter, into: &result) else { return nil }
return result as String?
}
func scanUpTo(_ characters: CharacterSet) -> String? {
var result: NSString? = nil
guard scanUpToCharacters(from: characters, into: &result) else { return nil }
return result as String?
}
}
With this we can write the parse function like this:
func parse(_ list: String) -> [String: String]? {
let scanner = Scanner(string: list)
guard scanner.scanString("[") else { return nil }
var result: [String: String] = [:]
let endOfPair: CharacterSet = [",", "]"]
repeat {
guard
let key = scanner.scanUpTo("="),
scanner.scanString("="),
let value = scanner.scanUpTo(endOfPair)
else {
return nil
}
result[key] = value
} while scanner.scanString(",")
guard scanner.scanString("]") else { return nil }
return result
}

Swift Get Next Page from header of NSHTTPURLResponse

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.

How to get only first letter from last name of full name?

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

Path extractions swift 3.0

I have a file path ...
/acme101/acmeX100/acmeX100.008.png
I can use this to get the extension .png in this case
let leftSide = (lhs.fnName as NSString).pathExtension
And this to get the filename acmeX100
let leftSide = (lhs.fnName as NSString).lastPathComponent
But I want the bit in the middle... the 008 in this case?
Is there a nice one liner?
Assuming the filepath takes that general form then this is (almost) a one-liner (I like to play it safe):
var filePath = "/acme101/acmeX100/acmeX100.008.png"
func extractComponentBetweenDots(inputString: String) -> String? {
guard inputString.components(separatedBy: ".").count > 2 else { print("Incorrect format") ; return nil } // Otherwise not in the correct format, you caa add other tests
return inputString.components(separatedBy: ".")[inputString.components(separatedBy: ".").count - 2]
}
Use as follows:
if let extractedString : String = extractComponentBetweenDots(inputString: filePath) {
print(extractedString)
}
I wanted to make an example using the same technique as in your question - despite the fact that the downcasting to NSString makes the whole thing rather ugly, it works efficiently. This is in Swift 3 but it would be easy to port it back to Swift 2 if needed.
func getComponents(from str: String) -> (name: String, middle: String, ext: String) {
let compo = (str as NSString).lastPathComponent as NSString
let ext = compo.pathExtension
let temp = compo.deletingPathExtension as NSString
let middle = temp.pathExtension
let file = temp.deletingPathExtension
return (name: file, middle: middle, ext: ext)
}
let result = getComponents(from: "/acme101/acmeX100/acmeX100.008.png")
print(result.name) // "acmeX100"
print(result.middle) // "008"
print(result.ext) // "png"
If you only need the middle part:
func pluck(str: String) -> String {
return (((str as NSString).lastPathComponent as NSString).deletingPathExtension as NSString).pathExtension
}
pluck(str: "/acme101/acmeX100/acmeX100.008.png") // "008"
Bon,
Sparky thanks for your answer. I ended up with this .. which is the same and yet different.
func pluck(str:String) -> String {
if !str.isEmpty {
let bitZero = str.characters.split{$0 == "."}.map(String.init)
if (bitZero.count > 2) {
let bitFocus = bitZero[1]
print("bitFocus \(bitFocus)")
return(bitFocus)
}
}
return("")
}