How to get top level domain name of URL in swift 4? - ios

My requirement is to get domain name of URL by filtering out it's subdomain name.
i can get host name by using code as below
if let url = URL(string: "https://blog.abc.in/") {
if let hostName = url.host {
print("host name = \(hostName)") // output is: blog.mobilock.in
}
}
so here in URL blog is a subdomain and abc is a domain name, I wish to know/print only abc by excluding its subdomain parts.
In android, there is a class InternetDomainName which return domain name, the similar solution I am looking for iOS
I tried several answers and it's not duplicate of any or some of them is not working or that is a workaround.
Get the domain part of an URL string?

So finally i found better and standard approach for this issue -
Mozilla volunteers maintain Public Suffix List and there you can find list of library for respective language.
so in list Swift library is also present.
At the time of writing this answer Swift library don't have provison of adding it through CocoPods so you have to add downloaded project directly into your project. Code to get TLD name assuming Swift library is added into your project.
import DomainParser
static func getTLD(withSiteURL:String) -> String? {
do{
let domainParse = try DomainParser()
if let publicSuffixName = domainParse.parse(host: withSiteURL)?.publicSuffix {
if let domainName = domainParse.parse(host: withSiteURL)?.domain {
let tldName = domainName.replacingOccurrences(of: publicSuffixName, with: "").replacingOccurrences(of: ".", with: "")
print("top level name = \(tldName)")
return tldName
}
}
}catch{
}
return nil
}
Add Domain parser library as sub-project of your project, as pod of this library is not available yet

It is just a workaround but works perfectly:
if let url = URL(string: "https://x.y.z.a.b.blog.mobilock.in/") {
if let hostName = url.host {
print("host name = \(hostName)") // output is: x.y.z.a.b.blog.mobilock.in
let subStrings = hostName.components(separatedBy: ".")
var domainName = ""
let count = subStrings.count
if count > 2 {
domainName = subStrings[count - 2] + "." + subStrings[count - 1]
} else if count == 2 {
domainName = hostName
}
print(domainName)
}
}
Let me know if you face any issue.

There is no simple way, regardless of language. See How to extract top-level domain name (TLD) from URL for some good discussion of the difficulties involved.

To fetch the root domain of a URL, you can use the following URL extension:
extension URL {
var rootDomain: String? {
guard let hostName = self.host else { return nil }
let components = hostName.components(separatedBy: ".")
if components.count > 2 {
return components.suffix(2).joined(separator: ".")
} else {
return hostName
}
}
}

Related

iOS user with language of "en", no region code, translation lookup fails

We have a bug reported where a user has a device with an en language and nil region code, and thus all NSLocalizedString lookups in are failing, meaning our string key is what is rendered onscreen. Thus, if we had this in our en.lproj/Localizable.strings file:
"some_key" = "Some string.";
It would render some_key instead of Some string. in our UI.
First question: how do I replicate this scenario locally? This question on Stack seems to almost describe the issue, but does not describe how one enters this state.
Second question: why would iOS not fall back to English in the event the region code was nil?
Second question: why would iOS not fall back to English in the event
the region code was nil?
The cause can be "There is no base development language that is enabled". Or it is iOS logic.
Here is my solution for Localization. I just want to share with you an alternative solution if it can help you to solve the issue.
public enum AppResourceLang: String {
case en
case vi
}
public class AppResManager {
public static let shared = AppResManager()
public var lang = "en"
var textBundle: Bundle!
public var mainBundle: Bundle!
private init() {
let mainBundleId = Bundle.main.bundleIdentifier!
// we use mainBundle's bundleIdentifier because normally your running target will contains "lproj" in Copy Bundle Resource
// If your text/image is located on different project/target or framework, you need to enter that target's bundleId.
mainBundle = Bundle(identifier: mainBundle)!
let path = mainBundle.path(forResource: lang, ofType: "lproj")
textBundle = Bundle(path: path!)
}
public func changeLang(code: String) {
if let path = mainBundle.path(forResource: code, ofType: "lproj") {
lang = code; textBundle = Bundle(path: path)
} else {
// fallback to English
lang = "en"
let path = mainBundle.path(forResource: lang, ofType: "lproj")
textBundle = Bundle(path: path!)
}
}
}
Then we can use above textBundle like below:
public extension String {
var localText: String {
guard let bundle = AppResManager.shared.textBundle else { return self }
return NSLocalizedString(self, bundle: bundle, comment: "")
}
var lTextUpcased: String {
guard let bundle = AppResManager.shared.textBundle else { return self.uppercased() }
return NSLocalizedString(self, bundle: bundle, comment: "").uppercased()
}
}
Here is my AppResource (like a framework). We can see I have Localizable.strings and it is localized for EN, VI.
Here is the real file/folder structure, you can see if you check on English in Localization for *.string, *.storyboard,... file. It will be cloned and saved in this folder (ex: en.lproj). You can base on this to point to the corresponding resource file.
Then, how to use my above codes. It is just a way to allow you to completely control the Localization.
public enum AppLanguage: String {
case en
case vi
}
// MARK: - Device info
public static func getDeviceLang() -> AppLanguage {
guard let languageCode = Locale.current.languageCode else { return .en }
let appLang = AppLanguage(rawValue: languageCode.lowercased()) ?? .en
return appLang
}
// Then this will switch the language of your resource based on device language.
AppResManager.shared.changeLang(code: YourGlobalClass.getDeviceLang().rawValue)
// Then use the above string extension to load the corresponding text
// Anywhere in your project.
let text = "your_key".localText

How to get an auth code using ASWebAuthenticationSession?

I'm trying to obtain an auth code from Stripe's OAuth endpoint using ASWebAuthenticationSession - this event happens after the my Stripe redirect url gets called.
Unfortunately, the authSession's completion handler doesn't call back with a callbackURL. And I need this callbackURL to query the auth code. I've read different articles on this topic but I can't figure out why my implementation doesn't work they way I expect it to.
Here's my code:
class CreateStripeConnectAccountController: UIViewController {
var authSession: ASWebAuthenticationSession!
override func viewDidLoad() {
super.viewDidLoad()
configureAuthSession()
}
private func configureAuthSession() {
let urlString = Constants.URLs.stripeConnectOAuth // Sample URL
guard let url = URL(string: urlString) else { return }
let callbackScheme = "myapp:auth"
authSession = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackScheme, completionHandler: { (callbackURL, error) in
guard error == nil, let successURL = callbackURL else {
print("Nothing")
return
}
let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "code"}).first
print(successURL.absoluteString)
})
authSession.presentationContextProvider = self
authSession.start()
}
}
extension CreateStripeConnectAccountController: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
self.view.window ?? ASPresentationAnchor()
}
}
I believe the issue is that you are giving nil for callbackURLScheme. You need to give a URL scheme that redirects to your app:
See apple's authentication example: https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service
And here's apple's docs on how to create a custom URL scheme for your app: https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app.
I know it's old, but anyway.
Make sure, that callbackScheme and scheme that is used in redirect_uri are the same.
Your callbackScheme myapp:auth is incorrect format.
The symbol : cannot be used in the scheme name of a URI.
See the following RFC definition of Scheme.
Scheme names consist of a sequence of characters beginning with a
letter and followed by any combination of letters, digits, plus
("+"), period ("."), or hyphen ("-").
https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
Thefore, revising the callbackURLscheme as myapp-auth or myapp.auth works well.
try setting your callbackScheme = "myapp" to receive callback
and from your server-side it should return "myapp://auth?token=1234"
Hope it helps.

Is there a way for Gmail to recognize line breaks from text from UIapplication.shared.open url

Gmail client does not recognize line breaks within text from UIApplication.shared.openURL(url)
I have a function that returns a tuple of available email clients (validated by UIApplication.shared.canOpen) and the associated URL. It's for an error reporting feature, so the arguments array contains the text that will autopopulate email fields.
There are no issues with launching any of the three email clients, but gmail is the only one that doesn't process the line breaks. Does gmail use a different method?
enum EmailClient {
case gmail
case outlook
case mail
var title: String {
switch self {
case .gmail: return "Gmail"
case .outlook: return "Outlook"
case .mail: return "Mail"
}
}
//Creates url used by UIapplciation.shared to launch the client and autopopulate the email
func url(error: CustomError?) -> URL? {
guard let username = CredentialManager.username,
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
return nil
}
let arguments = [
username,
UIDevice().modelName,
UIDevice.current.systemVersion,
appVersion,
error?.description ?? "N/A" //When called from the settings page, no error is passed in
]
var urlFormat: String
switch self {
case .gmail: urlFormat = "googlegmail:///co?to=%#&subject=%#&body=%#"
case .outlook: urlFormat = "ms-outlook://compose?to=%#&subject=%#&body=%#"
case .mail: urlFormat = "mailto:%#?subject=%#&body=%#"
}
return URL(string: String(format: urlFormat, arguments: [
EMAIL_RECIPIENT,
EMAIL_SUBJECT.replacingOccurrences(of: " ", with: "%20"),
String(format: EMAIL_BODY_FORMAT, arguments: arguments).replacingOccurrences(of: " ", with: "%20").replacingOccurrences(of: "\n", with: "%0A")
]))
}
}
It seems that using "\r\n" instead of "\n" fixes the problem
1) Add the the scheme to your info.plist
We can do this through the beautiful thing that is the Info.plist file. Add a new key called LSApplicationQueriesSchemes as an array. Then you can enter your apps within the array. The Mail app doesn’t need to go in here, presumably because it is an Apple app. Your entry should look like the below
func openGmail(withFrom: String?, withSubject: String?) {
var gmailUrlString = "googlegmail:///"
if let from = withFrom {
gmailUrlString += "co?to=\(from)"
}
if let subject = withSubject {
gmailUrlString += "&subject=\(subject)"
}
}
One last thing we will need to do is URL encode the subject line before we pass it into the URL. We can do this by calling subjectString?.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed) on the string.

Change URL last component name and add extension to the renamed one

I have the following URL
file:///Users/ivancantarino/Library/Developer/CoreSimulator/Devices/CCE2FCA5-05F0-4BB7-9A25-CBC168398A62/data/Containers/Data/Application/E48179F9-84FB-4CB3-B993-1E33FCFB5083/Library/Caches/CloudKit/34af81edf8344be1cf473ef394e06ccc8e1bc8cf/Assets/AE86E028-E4E0-45D5-B5FE-BAE975D07F2A.0166f7df06d2562d8d53e70a3779a46de3769584c3
I'm trying to rename the last part component of this url, but still having a url not only the filename.
I've managed to access the last part with the following code:
func extractAndBreakFilenameInComponents(fileURL: NSURL) {
let fileURLParts = fileURL.path!.components(separatedBy: "/")
let fileName = fileURLParts.last
print("filename:", fileName)
// prints: AE86E028-E4E0-45D5-B5FE-BAE975D07F2A.0166f7df06d2562d8d53e70a3779a46de3769584c3
}
I want to change the fileName to something like myFile.pdf but keeping the URL
It is a simple URL rename, not creating a new one, just changing the existing one
What's the best approach on this?
Thank you
You can also do with this way
var url = URL.init(string: "http://www.example.com/test123")
url?.deleteLastPathComponent()
url?.appendPathComponent("file.pdf")
print(url)
Output :http://www.example.com/file.pdf
Hope it helps to you
EDIT
You need to rename file use following code
1) Create Copy and update last component
var urlNew = URL.init(string: "http://www.example.com/test123")
urlNew?.deleteLastPathComponent()
urlNew?.appendPathComponent("file.pdf")
do {
try FileManager.default.moveItem(atPath: oldURL!, toPath: urlNew)
} catch {
}
You can do it this way
let fileName = fileURLParts.last
var newPath = ""
for i in 0 ..< fileURLParts.count
{
if i == (fileURLParts.count-1) {
newPath.append(fileName)
}else{
newPath.append(fileURLParts[i])
}
}

Swift URL query string get parameters

I'm using the OAuthSwift library to authenticate users of my app, but something doesn't seem to be working as it throws an error. After some debugging it seems to be going wrong on this line: parameters = url.query!.parametersFromQueryString()
It does have the url query string (set to url.query) but it somehow fails parsing the parametersFromQueryString(). Does someone else have this problem (with this library) and how did you solve it?
The "parametersFromQueryString" function can be found here: https://github.com/dongri/OAuthSwift/blob/1babc0f465144411c0dd9721271f239685ce83a9/OAuthSwift/String%2BOAuthSwift.swift
Create an extension of URL class and paste this code:
import Foundation
extension URL {
public var parametersFromQueryString : [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
}
}}
Now you can get the value of this calculated attribute from URL object by simply using:
url.parametersFromQueryString
Have you checked that url.query returns anything? and that it is url decoded before/after calling url.query?
try looking at the answers here:
URL decode in objective-c
If the method is looking for characters such as '?' or '=' and the URL is still encoded, it might not find them
Here is the same code as it should look like in swift:
let URLString = "http://sound17.mp3pk.com/indian/barfi/%5BSongs.PK%5D%20Barfi%20-%2001%20-%20Barfi!.mp3"
let URL = NSURL(string:url)
let filename = URL.path.lastPathComponent.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
//=> [Songs.PK] Barfi - 01 - Barfi!.mp3

Resources