Substring everything after match - swift

So I'm trying to substring everything after a match, but I'm only getting a single char for some reason.
let url = "http://localhost:3000/something/?page=2&per_page=20"
if let pageNumber = url.range(of: "per_page=", options: .backwards) {
// this prints "0" ? was kinda hoping for "20"
print(url.substring(from: pageNumber.upperBound))
}
Edit: Ok I changed the string to per_page without the "=" and now it works. No idea why.
.. url.range(of: "per_page", ..

In Swift 4 this code is supposed to work
let url = "http://localhost:3000/something/?page=2&per_page=20"
if let pageNumber = url.range(of: "per_page=", options: .backwards) {
print(url[pageNumber.upperBound...])
}
However if the string represents an URL I recommend the URLComponents API.
Create an URLComponents instance from the URL
Get the QueryItems
Filter the desired key
Get the value
let url = "http://localhost:3000/something/?page=2&per_page=20"
if let urlComponents = URLComponents(string: url),
let queryItems = urlComponents.queryItems,
let perPage = queryItems.first(where: {$0.name == "per_page"}) {
print(perPage.value!)
}

Related

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.

URL Builder in Swift

I am newbie on Swift and I am building API URL string as follows before pass it to URLSession.
I wonder there is a better way of doing it?
let jsonUrlString = Constants.API_URL + "/" + Constants.PATH + "/"
+ String(page)+"/" + Constants.NUMBER_ITEMS_PER_PAGE
The proper way to build a URL is to use URL and URLComponents. Simply appending strings together is error prone and it does not properly escape special characters that you might have in your values.
Here's one possible solution using URL:
if let baseURL = URL(string: Constants.API_URL) {
let jsonURL = baseURL.appendingPathComponent(Constants.PATH)
.appendingPathComponent(String(page))
.appendingPathComponent(Constants.NUMBER_ITEMS_PER_PAGE)
// use jsonURL with your URLSession
}
Another option with URLComponents (this properly ensures special characters are encoded):
if let baseComps = URLComponents(string: Constants.API_URL) {
var components = baseComps
components.path = "/\(Constants.PATH)/\(page)/\(Constants.NUMBER_ITEMS_PER_PAGE)"
if let jsonURL = components.url {
// use jsonURL with your URLSession
}
}
Also there is another way in swift to build string is called interpolation and mostly this one is used by developers.
If you using this you don't have to take your Int or another type value into string, Because is automatic take value into string.
i.e
let myValue = 3
let intToString = "\(myValue)" // "3"
let doubleValue = 4.5
let doubleToString = "\(doubleValue)" // "4.5"
So you URL will be like as below
let jsonUrlString = "\(Constants.API_URL)/\(Constants.PATH)/\(page)/\(Constants.NUMBER_ITEMS_PER_PAGE)"

Split URL query in Swift

I have the following URL query:
encodedMessage=PD94bWwgdmVyNlPg%3D%3D&signature=kcig33sdAOAr%2FYYGf5r4HGN
How can I split the query to get the of encodedMessage and signature values?
The right way to achieve this is to work with URLComponents:
A structure designed to parse URLs based on RFC 3986 and to construct
URLs from their constituent parts.
By getting the url components host string and query​Items array, as follows:
if let urlComponents = URLComponents(string: "http://mydummysite.com?encodedMessage=PD94bWwgdmVyNlPg%3D%3D&signature=kcig33sdAOAr%2FYYGf5r4HGN"), let host = urlComponents.host, let queryItems = urlComponents.queryItems {
print(host) // mydummysite.com
print(queryItems) // [encodedMessage=PD94bWwgdmVyNlPg==, signature=kcig33sdAOAr/YYGf5r4HGN]
}
queryItems array contains URLQuery​Item objects, which have name and value properties:
if let urlComponents = URLComponents(string: "http://mydummysite.com?encodedMessage=PD94bWwgdmVyNlPg%3D%3D&signature=kcig33sdAOAr%2FYYGf5r4HGN"),let queryItems = urlComponents.queryItems {
// for example, we will get the first item name and value:
let name = queryItems[0].name // encodedMessage
let value = queryItems[0].value // PD94bWwgdmVyNlPg==
}
Also:
In case of you are getting the query without the full url, I'd suggest to do a pretty simple trick, by adding a dummy host as a prefix to your query string, as follows:
let myQuery = "encodedMessage=PD94bWwgdmVyNlPg%3D%3D&signature=kcig33sdAOAr%2FYYGf5r4HGN"
let myDummyUrlString = "http://stackoverflow.com?" + myQuery
if let urlComponents = URLComponents(string: myDummyUrlString),let queryItems = urlComponents.queryItems {
// for example, we will get the first item name and value:
let name = queryItems[0].name // encodedMessage
let value = queryItems[0].value // PD94bWwgdmVyNlPg==
} else {
print("invalid url")
}
You can get the key value pairs this way:
let str = "encodedMessage=PD94bWwgdmVyNlPg%3D%3D&signature=kcig33sdAOAr%2FYYGf5r4HGN"
let arr = str.components(separatedBy:"&")
var data = [String:Any]()
for row in arr {
let pairs = row.components(separatedBy:"=")
data[pairs[0]] = pairs[1]
}
let message = data["encodedMessage"]
let sig = data["signature"]
I am not sure if that's what you were looking for or not. If it is not, could you please clarify a bit further as to what you are looking for?

Conversion stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) from Swift2 to Swift3 [duplicate]

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 to get the video ID of a YouTube string

(XCode 6.3.2, Swift 1.2)
After researching the internet for the whole evening I already know that this can't be done that easily.
I simply want to get the video ID of a YouTube link. So the "ABCDE" in "https://www.youtube.com/watch?v=ABCDE"
What I got so far:
var url = "https://www.youtube.com/watch?v=ABCDE"
let characterToFind: Character = "="
let characterIndex = find(url, characterToFind)
println(characterIndex)
url.substringFromIndex(advance(url.startIndex, characterIndex))
Prinln outputs: Optional(31)
That's right but I can't use this because it's no index.
XCode also states for the last line: Missing argument for parameter #3 in call
I also don't know what the 3rd parameter of substringFromIndex should be.
Many thanks in advance!
In your case there is no need to create an NSURL as other answers do. Just use:
var url = "https://www.youtube.com/watch?v=ABCDE"
if let videoID = url.componentsSeparatedByString("=").last {
print(videoID)
}
Swift 3+ version:
var url = "https://www.youtube.com/watch?v=ABCDE"
if let videoID = url.components(separatedBy: "=").last {
print(videoID)
}
You can use NSURL query property as follow:
Xcode 8.3.1 • Swift 3.1
let link = "https://www.youtube.com/watch?v=ABCDE"
if let videoID = URL(string: link)?.query?.components(separatedBy: "=").last {
print(videoID) // "ABCDE"
}
Another option is to use URLComponents:
let link = "https://www.youtube.com/watch?v=ABCDE"
if let videoID = URLComponents(string: link)?.queryItems?.filter({$0.name == "v"}).first?.value {
print(videoID) // "ABCDE"
}
Swift 1.x
let link = "https://www.youtube.com/watch?v=ABCDE"
if let videoID = NSURL(string: link)?.query?.componentsSeparatedByString("=").last {
println(videoID) // "ABCDE"
}
You need to unwrap the optional to use the index:
var url = "https://www.youtube.com/watch?v=ABCDE"
let characterToFind: Character = "="
if let index = find(url, characterToFind) { // Unwrap the optional
url.substringFromIndex(advance(index, 1)) // => "ABCDE"
}
find returns an optional – because the character might not be found, in which case it will be nil. You need to unwrap it to check it isn’t and to get the actual value out.
You can also use a range-based subscript on the index directly, rather than using advance to turn it into an integer index:
let url = "https://www.youtube.com/watch?v=ABCDE"
if let let characterIndex = find(url, "=") {
let value = url[characterIndex.successor()..<url.endIndex] // ABCDE
}
else {
// error handling, if you want it, here
}
You have more options if there is a reasonable default in the case of “not found” For example, if you just want an empty string in that case, this will work:
let idx = find(url, "=")?.successor() ?? url.endIndex
let value = url[idx..<url.endIndex]
Or, maybe you don’t even need to deal with the optionality right now, so you’re happy to leave the result optional as well:
// value will be Optional("ABCD")
let value = find(url, "=").map { url[$0.successor()..<url.endIndex] }
For a rundown on optionals, take a look here and here.
Also, rather than hand-parsing URL strings at all, you might find the info here useful.
With your url format, you can get the 'v' parameter's value by converting to NSURL then get parameters and separate it by '='.
var url: NSURL = NSURL(string: "https://www.youtube.com/watch?v=RAzOOfVA2_8")!
url.query?.componentsSeparatedByString("=").last
url.query returns v=RAzOOfVA2_8
If the link has more than 1 parameter, you can get all parameters then do a loop to verify each parameter:
var url: NSURL = NSURL(string: "https://www.youtube.com/watch?v=RAzOOfVA2_8")!
var params = url.query?.componentsSeparatedByString("&")
if let _params = params { //have parameters in URL
for param in _params { //verify each pair of key & value in your parameters
var _paramArray = param.componentsSeparatedByString("=") //separate by '=' to get key & value
if (_paramArray.first?.compare("v", options: nil, range: nil, locale: nil) == NSComparisonResult.OrderedSame) {
println(_paramArray.last)
}
}
} else {
//url does not have any parameter
}