This question already has answers here:
Swift - encode URL
(19 answers)
Closed 1 year ago.
I have a string that can include any characters, from alphanumeric to special chars like "&,.:/!" etc. I need to encode that string and send it to twitter. I tried the following but as soon as theres an & character in text, it doesn't work properly:
static func shareToTwitter(text: String) {
guard let urlEscaped = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
return
}
guard let url = URL(string: "twitter://post?message=\(urlEscaped)") else {
return
}
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
guard let twitterUrl = URL(string: "https://twitter.com/intent/tweet?text=\(urlEscaped)") else {
return
}
UIApplication.shared.open(twitterUrl)
}
}
So an example sentence might be: "I'm here & there". Instead, Twitter will receive "I'm here". How can I fix this?
As suggested by Alexander the most reliable way is to use URLComponents and URLQueryItem
static func shareToTwitter(text: String) {
var components = URLComponents(string: "twitter://post")!
components.queryItems = [URLQueryItem(name: "message", value: text)]
if let url = components.url, UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
components = URLComponents(string: "https://twitter.com/intent/tweet")!
components.queryItems = [URLQueryItem(name: "text", value: text)]
guard let twitterUrl = components.url else { return }
UIApplication.shared.open(twitterUrl)
}
}
Related
I am creating a manual Firebase Dynamic Link
https://firebase.google.com/docs/dynamic-links/create-manually
I have a url in the format of https://test.page.link/?link=https://test.page.link/register?accessCode%3DAA000000&apn=com.test.foo&ibi=com.test.bar&ofl=https://www.google.com.
How can I get the value of accessCode?
extension URL {
func valueOf(_ queryParameterName: String) -> String? {
guard let url = URLComponents(string: self.absoluteString) else { return nil }
return url.queryItems?.first(where: { $0.name == queryParameterName })?.value
}
}
Using this extension the query items are link, apn, and ofl.
url.valueOf("link") returns:
https://test.page.link/?link=https://test.page.link/register?accessCode=AA000000
I only need the AA000000 returned.
Since link is of the format of a url, create a URL instance from it and then get the accessCode value using valueOf(_:) method like so,
if let link = url.valueOf("link"), let linkUrl = URL(string: link) {
let accessCode = linkUrl.valueOf("accessCode")
print(accessCode)
}
You have an URL inside an URL, more precisely, an URL inside a value of a query, so what about using:
extension URL {
func valueOf(_ queryParameterName: String) -> String? {
guard let url = URLComponents(string: self.absoluteString) else { return nil }
guard let queryItems = url.queryItems else { return nil }
for aQueryItem in queryItems {
if aQueryItem.name == queryParameterName {
return aQueryItem.value
} else if let subvalue = aQueryItem.value, let subURL = URL(string: subvalue), let found = subURL.valueOf(queryParameterName) {
return found
}
}
return nil
}
}
Now you can use url.valueOf("accessCode")
I'm building an app that can receive Firebase's Dynamic Links and redirects it into a certain UIViewController. I've followed the steps on this video, but there's something that I still don't understand. So after I followed the steps above, I print my link on the debug area to something like this:
Incoming link parameter is https://play.google.com/store/apps/details?id=com.xxx.xxx&referrer=utm_source%3DXXX%26utm_medium%3Dxxx%26utm_campaign%3DXXX
Parameter id has a value of com.xxx.xxx
Parameter referrer has a value of utm_source=XXX&utm_medium=xxx&utm_campaign=XXX
This works perfectly, but what I want to do is I want to get the values of utm_source, utm_medium and utm_campaign and store it into a variable. I'm still uncertain on how do I manage to achieve this. Here is my code:
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
guard let url = dynamicLink.url else {
print("Dynamic Link has no URL")
return
}
print("Incoming link parameter is \(url.absoluteString)")
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { return }
for queryItem in queryItems {
print("Parameter \(queryItem.name) has a value of \(queryItem.value ?? "")")
}
}
So how do I manage to do this? Feel free to ask if you need any more information. Thank you.
I don't know if this is the best answer or the right code design but fortunately I managed to solve by changing handleIncomingDynamicLink function to something like this:
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
guard let url = dynamicLink.url else {
print("Dynamic Link has no URL")
return
}
print("Incoming link parameter is \(url.absoluteString)")
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { return }
for queryItem in queryItems {
if queryItem.name == "referrer" {
print("Referrer found")
let referrerString = "https://xxx.page.link/details?\(queryItem.value ?? "")"
let referrerUrl = URL(string: referrerString)
guard let queryItemComponents = URLComponents(url: referrerUrl!, resolvingAgainstBaseURL: false), let itemComponents = queryItemComponents.queryItems else {
print("Referrer string couldn't be casted as a URL")
return
}
for item in itemComponents {
print("Parameter \(item.name) has a value of \(item.value ?? "")")
}
}
}
}
So basically what I did was I take the referrer value of the main URL and make it a "custom URL" so that I could parse the values by using the URLComponents function. The output is exactly what I expected to be.
Incoming link parameter is https://play.google.com/store/apps/details?id=com.xxx.xxx&referrer=utm_source%3Dxxx%26utm_medium%3Dxxx%26utm_campaign%3Dxxx
Referrer found
Parameter utm_source has a value of xxx
Parameter utm_medium has a value of xxx
Parameter utm_campaign has a value of xxx
Here's is how you can make a struct and assign the values to it.
struct MyVariable {
var referrer: String?, utm_source: String?, utm_medium: String?, utm_campaign: String?
}
func handleIncomingDynamicLink(_ dynamicLink: URL?) {
guard let string = dynamicLink?.absoluteString.removingPercentEncoding,
let url = URL(string: string) else {
print("Dynamic Link has no URL")
return
}
var myVariable = MyVariable()
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems else { return }
for queryItem in queryItems {
if queryItem.name == "referrer" {
myVariable.referrer = queryItem.value
} else if queryItem.name == "utm_source" {
myVariable.utm_source = queryItem.value
} else if queryItem.name == "utm_medium" {
myVariable.utm_medium = queryItem.value
} else if queryItem.name == "utm_campaign" {
myVariable.utm_campaign = queryItem.value
}
}
print("MyVariable: \(myVariable)")
}
I am using amazon product advertising api for search product. Installed awscore and alamofire cocopods. Done functionality for getting signature and added parameters for item search to get product images, title and description in table view list.
Here is the code i tried for getting amazon search:
private func signedParametersForParameters(parameters: [String: String]) -> [String: String] {
let sortedKeys = Array(parameters.keys).sorted(by: <)
let query = sortedKeys.map { String(format: "%#=%#", $0, parameters[$0] ?? "") }.joined(separator: "&")
let stringToSign = "GET\nwebservices.amazon.in\n/onca/xml\n\(query)"
print("stringToSign::::\(stringToSign)")
let dataToSign = stringToSign.data(using: String.Encoding.utf8)
let signature = AWSSignatureSignerUtility.hmacSign(dataToSign, withKey: CameraViewController.kAmazonAccessSecretKey, usingAlgorithm: UInt32(kCCHmacAlgSHA256))!
var signedParams = parameters;
signedParams["Signature"] = urlEncode(signature)
print("urlencodesignature::\(urlEncode(signature))")
return signedParams
}
public func urlEncode(_ input: String) -> String {
let allowedCharacterSet = (CharacterSet(charactersIn: "!*'();:#&=+$,/?%#[] ").inverted)
if let escapedString = input.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) {
return escapedString
}
return ""
}
func send(url: String) -> String {
// activityIndicator.startAnimating()
guard let url = URL(string: url) else {
print("Error! Invalid URL!") //Do something else
// activityIndicator.stopAnimating()
return ""
}
print("send URL: \(url)")
let request = URLRequest(url: url)
let semaphore = DispatchSemaphore(value: 0)
var data: Data? = nil
URLSession.shared.dataTask(with: request) { (responseData, _, _) -> Void in
data = responseData
print("send URL session data: \(String(describing: data))")
let parser = XMLParser(data: data!)
parser.delegate = self as? XMLParserDelegate
if parser.parse() {
print(self.results ?? "No results")
}
semaphore.signal()
}.resume()
// activityIndicator.stopAnimating()
semaphore.wait(timeout: .distantFuture)
let reply = data.flatMap { String(data: $0, encoding: .utf8) } ?? ""
return reply
}
public func getSearchItem(searchKeyword: String) -> [String:AnyObject]{
let timestampFormatter: DateFormatter
timestampFormatter = DateFormatter()
timestampFormatter.timeZone = TimeZone(identifier: "GMT")
timestampFormatter.dateFormat = "YYYY-MM-dd'T'HH:mm:ss'Z'"
timestampFormatter.locale = Locale(identifier: "en_US_POSIX")
// let responsegroupitem: String = "ItemAttributes"
// let responsegroupImages:String = "Images"
// activityIndicator.startAnimating()
let operationParams: [String: String] = [
"Service": "AWSECommerceService",
"Operation": "ItemSearch",
"ResponseGroup": "Images,ItemAttributes",
"IdType": "ASIN",
"SearchIndex":"All",
"Keywords": searchKeyword,
"AWSAccessKeyId": urlEncode(CameraViewController.kAmazonAccessID),
"AssociateTag": urlEncode(CameraViewController.kAmazonAssociateTag),
"Timestamp": urlEncode(timestampFormatter.string(from: Date()))]
let signedParams = signedParametersForParameters(parameters: operationParams)
let query = signedParams.map { "\($0)=\($1)" }.joined(separator: "&")
let url = "http://webservices.amazon.in/onca/xml?" + query
print("querydata::::\(query)")
let reply = send(url: url)
print("reply::::\(reply)")
// activityIndicator.stopAnimating()
return [:]
}
Created bridging header file #import .
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
getSearchItem(searchKeyword: searchKeyword)
}
Here is my console output:
My issue is when tapping search button product searched was not listing. What mistake done i don't know. Can anyone help me out of this pls..
According to the documentation:
The HTTPRequestURI component is the HTTP absolute path component of the URI up to, but not including, the query string. If the HTTPRequestURI is empty, use a forward slash ( / ).
HTTPRequestURI is always "/onca/xml" for Product Advertising API. HTTPVerb is either GET or POST.
Try just setting requestURL to "/onca/xml" instead of the full URL you shouldn't be sending the full URL or the query string in this part.
Also you need to percent encode the values that you are sending. You are sending commas in the response group property which should be percent encoded
let operationParams: [String: String] = [
"Service": "AWSECommerceService",
"Operation": "ItemSearch",
"ResponseGroup": urlEncode("Images,ItemAttributes"),
"IdType": "ASIN",
"SearchIndex":"All",
"Keywords": urlEncode(searchKeyword),
"AWSAccessKeyId": urlEncode(CameraViewController.kAmazonAccessID),
"AssociateTag": urlEncode(CameraViewController.kAmazonAssociateTag),
"Timestamp": urlEncode(timestampFormatter.string(from: Date()))]
let stringToSign = "GET\n/onca/xml\n\(query)"
Note: You should be using https instead of http
I am trying to get the parameters from a URL using Swift. Let's say I have the following URL:
http://mysite3994.com?test1=blah&test2=blahblah
How can I get the values of test1 and test2?
You can use the below code to get the param
func getQueryStringParameter(url: String, param: String) -> String? {
guard let url = URLComponents(string: url) else { return nil }
return url.queryItems?.first(where: { $0.name == param })?.value
}
Call the method like let test1 = getQueryStringParameter(url, param: "test1")
Other method with extension:
extension URL {
public var queryParameters: [String: String]? {
guard
let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems else { return nil }
return queryItems.reduce(into: [String: String]()) { (result, item) in
result[item.name] = item.value
}
}
}
Step 1: Create URL extension
extension URL {
func valueOf(_ queryParameterName: String) -> String? {
guard let url = URLComponents(string: self.absoluteString) else { return nil }
return url.queryItems?.first(where: { $0.name == queryParameterName })?.value
}
}
Step 2: How to use the extension
let newURL = URL(string: "http://mysite3994.com?test1=blah&test2=blahblah")!
newURL.valueOf("test1") // Output i.e "blah"
newURL.valueOf("test2") // Output i.e "blahblah"
I also made a URL extension, but put the query param lookup into a subscript.
extension URL {
subscript(queryParam:String) -> String? {
guard let url = URLComponents(string: self.absoluteString) else { return nil }
return url.queryItems?.first(where: { $0.name == queryParam })?.value
}
}
Usage:
let url = URL(string: "http://some-website.com/documents/127/?referrer=147&mode=open")!
let referrer = url["referrer"] // "147"
let mode = url["mode"] // "open"
It appears that none of existing answers work when the link leads to a web site created on Angular. This is because Angular's paths often include a # (hash) symbol in all links, which results in url.queryItems always returning nil.
If a link looks like this: http://example.com/path/#/morepath/aaa?test1=blah&test2=blahblah
Then the parameters can only be obtained from url.fragment. With some additional parsing logic added to #Matt's extension, a more universal code would look like this:
extension URL {
subscript(queryParam: String) -> String? {
guard let url = URLComponents(string: self.absoluteString) else { return nil }
if let parameters = url.queryItems {
return parameters.first(where: { $0.name == queryParam })?.value
} else if let paramPairs = url.fragment?.components(separatedBy: "?").last?.components(separatedBy: "&") {
for pair in paramPairs where pair.contains(queryParam) {
return pair.components(separatedBy: "=").last
}
return nil
} else {
return nil
}
}
}
Usage remains same:
let url = URL(string: "http://example.com/path/#/morepath/aaa?test1=blah&test2=blahblah")!
let referrer = url["test1"] // "blah"
let mode = url["test2"] // "blahblah"
Another way of doing this is to create an extension on URL to return the components, and then create an extension on [URLQueryItem] to retrieve the value from the queryItems.
extension URL {
var components: URLComponents? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)
}
}
extension Array where Iterator.Element == URLQueryItem {
subscript(_ key: String) -> String? {
return first(where: { $0.name == key })?.value
}
}
And this is an example of how this could be used:
if let urlComponents = URL(string: "http://mysite3994.com?test1=blah&test2=blahblah")?.components,
let test1Value = urlComponents.queryItems?["test1"] {
print(test1Value)
}
I try to get URLs in text. So, before, I used such an expression:
let re = NSRegularExpression(pattern: "https?:\\/.*", options: nil, error: nil)!
But I had a problem when a user input URLs with Capitalized symbols (like Http://Google.com, it doesn't match it).
I tried:
let re = NSRegularExpression(pattern: "(h|H)(t|T)(t|T)(p|P)s?:\\/.*", options: nil, error: nil)!
But nothing happened.
You turn off case sensitivity using an i inline flag in regex, see Foundation Framework Reference for more information on available regex features.
(?ismwx-ismwx)
Flag settings. Change the flag settings. Changes apply to the portion of the pattern following the setting. For example, (?i) changes to a case insensitive match.The flags are defined in Flag Options.
For readers:
Matching an URL inside larger texts is already a solved problem, but for this case, a simple regex like
(?i)https?://(?:www\\.)?\\S+(?:/|\\b)
will do as OP requires to match only the URLs that start with http or https or HTTPs, etc.
Swift 4
1. Create String extension
import Foundation
extension String {
var isValidURL: Bool {
guard !contains("..") else { return false }
let head = "((http|https)://)?([(w|W)]{3}+\\.)?"
let tail = "\\.+[A-Za-z]{2,3}+(\\.)?+(/(.)*)?"
let urlRegEx = head+"+(.)+"+tail
let urlTest = NSPredicate(format:"SELF MATCHES %#", urlRegEx)
return urlTest.evaluate(with: trimmingCharacters(in: .whitespaces))
}
}
2. Usage
"www.google.com".isValidURL
Try this - http?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?
let pattern = "http?://([-\\w\\.]+)+(:\\d+)?(/([\\w/_\\.]*(\\?\\S+)?)?)?"
var matches = [String]()
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
let nsstr = text as NSString
let all = NSRange(location: 0, length: nsstr.length)
regex.enumerateMatchesInString(text, options: NSMatchingOptions.init(rawValue: 0), range: all, usingBlock: { (result, flags, _) in
matches.append(nsstr.substringWithRange(result!.range))
})
} catch {
return [String]()
}
return matches
Make an exension of string
extension String {
var isAlphanumeric: Bool {
return rangeOfString( "^[wW]{3}+.[a-zA-Z]{3,}+.[a-z]{2,}", options: .RegularExpressionSearch) != nil
}
}
call using like this
"www.testsite.edu".isAlphanumeric // true
"flsd.testsite.com".isAlphanumeric //false
My complex solution for Swift 5.x
ViewController:
private func loadUrl(_ urlString: String) {
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
}
UISearchBarDelegate:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text else { return }
if !text.isUrl() {
let finalUrl = String(format: "%#%#", "https://www.google.com/search?q=", text)
loadUrl(finalUrl)
return
}
if text.starts(with: "https://") || text.starts(with: "http://") {
loadUrl(text)
return
}
let finalUrl = String(format: "%#%#", "https://", text)
loadUrl(finalUrl)
}
String extension:
extension String {
func isUrl() -> Bool {
guard !contains("..") else { return false }
let regex = "((http|https)://)?([(w|W)]{3}+\\.)?+(.)+\\.+[A-Za-z]{2,3}+(\\.)?+(/(.)*)?"
let urlTest = NSPredicate(format:"SELF MATCHES %#", regex)
return urlTest.evaluate(with: trimmingCharacters(in: .whitespaces))
}
}