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

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])
}
}

Related

Save/create folder that it to be treated as a file with FileManager

I have a iOS/CatalystMacOS-app that can create, save, open custom text-files (with my own file extension). This works fine. However, now I need more than text. I want to save optional files in this file as well. Apparently macOS (and iOS?) can treat folders as files. But I cannot get it to work as wanted. The folder is still treated as a folder, even if it has a file extension.
This is the code I use to create the folder:
func showNewFilePathDialog(from viewController: UIViewController, saveCompleted: URLCallback?) {
guard !isPresenting else {
return
}
let objectToSave = ...
// Find an available filename
var number = 0
var exportURL: URL!
var data: Data!
var fullFileName = ""
while true {
let numberText = number == 0 ? "" : number.asString()
fullFileName = "baseFileName" + "\(numberText).myFileExtension"
exportURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(fullFileName)
let dict = objectToSave.toDict()
let json = dict.json!
data = json.data(using: .utf8)!
if FileManager.default.fileExists(atPath: exportURL.path) {
number += 1
continue
} else {
break
}
}
do {
try FileManager.default.createDirectory(atPath: exportURL.path, withIntermediateDirectories: true, attributes: nil)
} catch {
NSLog("Couldn't create document directory")
viewController.presentErrorDialog(from: error)
return
}
// 2. Create containing json file
do {
try data.write(to: exportURL.appendingPathComponent("content.json"))
} catch {
viewController.presentErrorDialog(from: error)
return
}
isPresenting = true
self.onSaveDialogComplete = saveCompleted
let pickerViewController = UIDocumentPickerViewController(url: exportURL, in: .exportToService)
pickerViewController.delegate = self
viewController.present(pickerViewController, animated: true)
}
And then it appears like this in macOS finder:
It will show up similar in iOS, not allowing me to open the folder as a single file either.
Edit: Using UIDocument/public.composite-content/FileWrapper seems to work as well, but the problem still consists: When viewed in macOS finder it is still treated as a folder. Also when trying to open the app from the open-dialog via UIDocumentPickerViewController trying to open the file-bundle only opens the folder and wont let me open it into the app :(
This is my info.list Export Type UTIs:
Edit2: Also tried with removing all but com.apple.package but does not work either. Still cannot open my custom type as it behaves like a folder.
Got it working. Seemed as old builds of my app was interfering with the system file types. So I searched for my app name and removed old builds from my computer. Then the system recognized my file suffix and opened it right away!
But I lost the icon this time, but that's another issue :)

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

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
}
}
}

Swift 3.0 FileManager.fileExists(atPath:) always return false

When I use method .fileExists(atPath:)to judge whether the file is exist in file system, the method always return false to me. I checked the file system and the file do exist. Here is my code:
let filePath = url?.path
var isDir : ObjCBool = false
if(self.fileManager.fileExists(atPath: filePath!, isDirectory: &isDir)){
let result = NSData(contentsOfFile: filePath!)
}
or
let filePath = url?.path
if(self.fileManager.fileExists(atPath: filePath!)){
let result = NSData(contentsOfFile: filePath!)
}
the if clause will always be skipped.
I assume your url is an URL type. If so try this out:
let filePath = url?.path // always try to work with URL when accessing Files
if(FileManager.default.fileExists(atPath: filePath!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
Saying enough, you should change your implementation like this:
if(FileManager.default.fileExists(atPath: (url?.path)!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
EDIT: 1
There is even more better way, you can call it swift-way (:D). You don't have to explicitly check for file existence.
guard let result = NSData(contentsOf: fileURL) else {
// No data in your fileURL. So no data is received. Do your task if you got no data
// Keep in mind that you don't have access to your result here.
// You can return from here.
return
}
// You got your data successfully that was in your fileURL location. Do your task with your result.
// You can have access to your result variable here. You can do further with result constant.
print(result)
Update for Swift 3.0+ without the Objective-Cish NS prefix:
do {
let result = try Data(contentsOf: fileURL)
print(result)
} catch {
print(error)
}
in swift 3
just in case anyone gets confused like i did, here's the full snippets:
let str = "file:///Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3"
let url = URL(string: str)
print(url!.path,"\n")
if FileManager.default.fileExists(atPath: url!.path) {
print("FILE Yes AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
this prints
/Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3
FILE Yes AVAILABLE
notice how the 'file://' got chopped off?
I want to share my experience, in case anyone else gets baffled by this.
Tested on iOS 10-11, Xcode 9.2 and Swift 3.2.
Short answer: if you save a file path to disk, you may solve by not including the Documents directory in it.
Instead, every time you need to retrieve the file with the saved path, get the Documents directory and append the path.
For an iOS app, I was saving an image to .../Documents/Pictures through the relative URL, let's say url.
As the image was saved, a path, let's say url.path, was saved too in a Core Data entity.
When I later tried retrieving the image through FileManager.default.fileExists(atPath: url.path), it always returned false.
I was testing the app on my iPhone. It turned out that, for some reason, every time I ran the app from Xcode, the app identifier folder changed!!
So:
App opened from Xcode -> Image saved -> app closed -> app opened from physical device ->
fileExists -> TRUE
App opened from Xcode -> Image saved -> app closed -> app opened from Xcode -> fileExists -> FALSE
You can check if this is your case by getting and printing the Document folder path (or URL, it doesn't matter) and comparing it with the saved path (or URL). If you get something like this:
/var/mobile/Containers/Data/Application/5D4632AE-C432-4D37-A3F7-ECD05716AD8A/Documents..
/var/mobile/Containers/Data/Application/D09904C3-D80D-48EB-ACFB-1E42D878AFA4/Documents..
you found the issue.
Just use path instead of absoluteString to remove file://
FileManager.default.fileExists(atPath: URL.init(string: "your_url")!.path)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
var path = paths[0] as String;
path = path + "/YourFilePath"
if((NSFileManager.defaultManager().fileExistsAtPath(path))) {
let result = NSData(contentsOfFile: filePath!)}
Try the above code and check again
I had the same problem this worked for me
filePath.replacingOccurrences(of: "file://", with: "")
First, what does your file path looks like? If the path begins with a ~,then it must be expanded with expandingTildeInPath;
Check if the path is inaccessible to your app. iOS App can only visits its sandbox directories.

How to get partial file path after downloading with Alamofire?

I want to save the path of where I downloaded a file to my model object. Based on this StackOverflow answer, I should "only store the filename, and then combine it with the location of the documents directory on the fly". Well in my case it's the Application Support directory. I am not sure how to do this.
The full path of where the file will be downloaded is: /Library/ApplicationSupport/com.Company.DemoApp/MainFolder/myFile.jpg
/Library/Application Support/ is the part I can't save anywhere and I have to get it during runtime from FileManager.
com.Company.DemoApp/MainFolder/myFile.jpg is part of that file path that I can store in database/config files.
However I do not know how to get this path: com.Company.DemoApp/MainFolder/myFile.jpg from Alamofire after downloading it. For example, here is the code to download the file, how do I get this path?
func destination(named name: String, pathExtension: String) -> DownloadRequest.DownloadFileDestination {
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let appSupportDirURL = FileManager.createOrFindApplicationSupportDirectory()
let fileURL = appSupportDirURL?.appendingPathComponent("com.Company.DemoApp/MainFolder/\(name).\(pathExtension)")
return (fileURL!, [.removePreviousFile, .createIntermediateDirectories])
}
return destination
}
let finalDestination = destination(named: image.title, pathExtension: image.preview.pathExtension)
Alamofire.download(urlString, to: finalDestination).response { response in
if let imagePath = response.destinationURL?.path {
/// I want to this path here: com.Company.DemoApp/MainFolder/myFile.jpg
/// But not sure how to get it. How do I get this path?
}
}
The problem is that Alamofire gives me the full path only: /Library/ApplicationSupport/com.Company.DemoApp/MainFolder/myFile.jpg. But I only want this part: com.Company.DemoApp/MainFolder/myFile.jpg.
Any ideas on how to get this path?
Also, Apple seems to refer to Bookmark's if you want to get a file at runtime here: Apple Docs for Bookmarks
Note this is a follow up to a previous question.
UPDATE 1
This is one way I think this could work. (Based of an answer above).
enum DataDirectory: String {
case feed = "com.Compnay.DemoApp/MainFolder/"
}
Alamofire.download(urlString, to: finalDestination).response { response in
let destURL = response.destinationURL!
/// One way to save this path is to do so like this:
myImageObject.partialLocalPath = "\(DataDirectory.feed.rawValue)\(destURL.lastPathComponent)"
}
}
So I am saving the part in an enum and appending the name to it once the download finishes - then I add this to my model object for saving.
Any thoughts?

FileManager.createDirectory fails with NSCocoaErrorDomain Code: 518

I'm doing
let tempDirectory = URL(string: "\(NSTemporaryDirectory())video/")!
do {
try FileManager.default.createDirectory(
at: tempDirectory,
withIntermediateDirectories: true)
} catch { report(error) }
and that's often throwing an NSCocoaErrorDomain Code: 518.
Any idea of the reason? I thought that could because there's already something there, so I added
var isDir: ObjCBool = false
if FileManager.default.fileExists(
atPath: tempDirectory.absoluteString,
isDirectory: &isDir
) {
if isDir.boolValue {
print("Temp directory exists on launch")
}
else {
print("Temp directory exists on launch and is a file")
}
return
}
but that doesn't seem to catch anything
Your building of tempDirectory isn't correct. You want:
let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()). appendingPathComponent("video")
The issue with your code is that you were not passing a value URL string to URL(string:). Since you have a file path you need to use URL(fileURLWithPath:). And build paths/URLs using the provided methods to ensure slashes and other parts are added correctly.
Print your value of tempDirectory from your original code and then print the new value from the code in my answer. Note the key difference.
Your URL will be something like:
/var/...
and it may be missing the slash before "video".
The correct file URL will be something like:
file:///var/...

Resources