stringByAppendingPathComponent is unavailable
Asked Answered
H

11

141

My app shares photo on Instagram, to do this it first saves it on a temporary directory:

let writePath = NSTemporaryDirectory().stringByAppendingPathComponent("instagram.igo")

It was working on Swift 1.2, but does not work on Swift 2.0.

Given error message is:

stringByAppendingPathComponent is unavailable: use URLByAppendingPathComponent on NSURL instead.

Hindenburg answered 10/9, 2015 at 12:16 Comment(0)
D
149

It looks like the method stringByAppendingPathComponent is removed in Swift 2.0, so what the error message suggests is to use:

let writePath = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("instagram.igo")

Update:

URLByAppendingPathComponent() has been replaced by appendingPathComponent() so instead do:

let writePath = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("instagram.igo")
Deckert answered 10/9, 2015 at 12:30 Comment(11)
if you are going to use this design that you will have problems like convert space to %20 Application%20SupportCarce
no, Swift 2.0 can use stringByAppendingPathComponent, see my answer below.Hendecasyllable
@JeffreyNeo yes, but that is not a NSURL method but an NSStringSibilla
@DánielNagy I means you said "stringByAppendingPathComponent is removed in Swift 2.0" is not correct, and @Hindenburg didn't ask for only NSURL method.Hendecasyllable
@JeffreyNeo actually, it is correct, since in Swift 1.2's String had a method called stringByAppendingPathComponent, but Swift 2.0's String doesn't. And NSString is not part of the Swift language, it's part of the Foundation framework.Sibilla
@DánielNagy well, NSString is same as NSURL as the foundation framework you mentioned. But they're both acceptable in Swift, like the way you answer this question. Reference: developer.apple.com/library/mac/documentation/Cocoa/Reference/… and developer.apple.com/library/mac/documentation/Cocoa/Reference/…Hendecasyllable
@JeffreyNeo yes, they are perfectly acceptable, I just wanted to make a point that the method stringByAppendingPathComponent was removed from Swift 2.0Sibilla
It's funny that NSTemporaryDirectory() should return "String" but it's a NSURL actually. Anyone can explain this?Senskell
@AllenLin it returns a String, I'm not sure about the comment "it's a NSURL actually". Why do you think i's an NSURL?Sibilla
@Dániel Nagy Because with the function "URLByAppendingPathComponent" it means "NSTemporaryDirectory()" returns NSURL, doesn't it? Btw, I am using Swift 2.2 right now, and it works with "stringByAppendingPathComponent" again.Senskell
@AllenLin but first you create an NSURL with NSURL(fileURLWithPath: NSTemporaryDirectory()), and that has the URLByAppendingPathComponent, not the String.Sibilla
C
74

It is working for NSString so you can use it like this:

extension String {
    func stringByAppendingPathComponent(path: String) -> String {
        let nsSt = self as NSString
        return nsSt.stringByAppendingPathComponent(path)
    }
}

Now you can use this extension which will convert your String to NSString first and then perform operation.

And your code will be:

let writePath = NSTemporaryDirectory().stringByAppendingPathComponent("instagram.igo")

Here is some another methods for use:

extension String {  

    var lastPathComponent: String {  
        return (self as NSString).lastPathComponent  
    }  
    var pathExtension: String {  
        return (self as NSString).pathExtension  
    }  
    var stringByDeletingLastPathComponent: String {  
        return (self as NSString).stringByDeletingLastPathComponent  
    }  
    var stringByDeletingPathExtension: String {  
        return (self as NSString).stringByDeletingPathExtension  
    }  
    var pathComponents: [String] {  
        return (self as NSString).pathComponents  
    }  
    func stringByAppendingPathComponent(path: String) -> String {  
        let nsSt = self as NSString  
        return nsSt.stringByAppendingPathComponent(path)  
    }  
    func stringByAppendingPathExtension(ext: String) -> String? {  
        let nsSt = self as NSString  
        return nsSt.stringByAppendingPathExtension(ext)  
    }  
}

Reference from HERE.

For swift 3.0:

extension String {
    func stringByAppendingPathComponent1(path: String) -> String {
        let nsSt = self as NSString
        return nsSt.appendingPathComponent(path)
    }
}

let writePath = NSTemporaryDirectory().stringByAppendingPathComponent(path: "instagram.igo")


extension String {

    var lastPathComponent: String {
        return (self as NSString).lastPathComponent
    }
    var pathExtension: String {
        return (self as NSString).pathExtension
    }
    var stringByDeletingLastPathComponent: String {
        return (self as NSString).deletingLastPathComponent
    }
    var stringByDeletingPathExtension: String {
        return (self as NSString).deletingPathExtension
    }
    var pathComponents: [String] {
        return (self as NSString).pathComponents
    }
    func stringByAppendingPathComponent(path: String) -> String {
        let nsSt = self as NSString
        return nsSt.appendingPathComponent(path)
    }
    func stringByAppendingPathExtension(ext: String) -> String? {
        let nsSt = self as NSString
        return nsSt.appendingPathExtension(ext)
    }
}
Causality answered 10/9, 2015 at 12:24 Comment(8)
While this is a valid solution, there is a reason why Apple has removed those methods - using paths to locating resources is deprecated and NSURLs should be used instead. Just saying.Jetsam
snippet: String( NSString( string: path ).stringByAppendingPathComponent( imageName ) ) ... otherwise, quite agreed with @CharlieMonroeLegibility
@CharlieMonroe if that's really the case why are there still a bunch of methods that don't accept an URL as a path, in the SDK?Superabundant
@JorisMans These are usually older methods (available since 10.0, or early after). Ever since sandboxing was introduced, there is no way of passing on a path with e.g. appscope bookmark - you need a URL instead. Apple is slow in updating APIs that only handful people use. Or do you have an example of a recently added API (the last 3-4 years)?Jetsam
I keep seeing this snippet of code in work projects. It is not the right solution. You can see at the link below wha the prescribed solution is from Apple. The top rated answer shows how this is done. Please down vote this answer. It is bad practice to bring NSString into Swift when the new String class handles unicode properly. github.com/apple/swift/blob/…Yuu
@CharlieMonroe, Then why isn't there a file exists at url method on FileManager?Kailey
@IulianOnofrei - Because you should be using checkResourceIsReachable() or checkPromisedItemIsReachable() on URL instead. FileManager is still an ObjC class NSFileManager with the NS prefix removed for Swift and fileExistsAtPath was there ever since OS X 10.0. The world has evolved since and as apps are sandboxed (which is less obvious on iOS), the file may exist, you just may not have permission to view it; also, the file may be in the cloud, etc. Which is why the simple BOOL method is replaced with something more complex for URL, but more semantically correct.Jetsam
@CharlieMonroe Too bad that these replacements are not mentioned in the docs.Kailey
H
30

Simply wrap your string as NSString.

let writePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("instagram.igo")
Hendecasyllable answered 21/9, 2015 at 2:1 Comment(1)
cool one.. String class dont have this but NSString exists! makes sense.Danell
R
19

for Swift 3:

let writePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(directoryname).path

or better create this extension:

extension String {
    func appendingPathComponent(_ string: String) -> String {
        return URL(fileURLWithPath: self).appendingPathComponent(string).path
    }
}

usage:

 let writePath = NSTemporaryDirectory().appendingPathComponent(directoryname)
Rarotonga answered 5/10, 2016 at 12:33 Comment(0)
L
6

Swift 3 Solution:

Here is a function to get the documents directory path-

    func getDocumentsDirectory() -> URL {
         let paths = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask)
         let documentsDirectory = paths[0]
         return documentsDirectory
     }

How to use:

    getDocumentsDirectory.appendingPathComponent("google.com")

Result:

    file:///var/folders/w1/3rcp2fvs1qv43hfsh5876s0h0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.MyPlayground-7CF9F706-509C-4D4C-997E-AB8FE9E4A6EA/Documents/google.com
Lusatia answered 7/10, 2016 at 6:59 Comment(0)
C
5

For swift 2.0

// Get the documents Directory
    func documentsDirectory() -> String {
        let documentsFolderPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
        return documentsFolderPath
    }

// Get path for a file in the directory
func fileInDocumentsDirectory(filename: String) -> String {

    let writePath = (documentsDirectory() as NSString).stringByAppendingPathComponent("Mobile")

    if (!NSFileManager.defaultManager().fileExistsAtPath(writePath)) {
        do {
            try NSFileManager.defaultManager().createDirectoryAtPath(writePath, withIntermediateDirectories: false, attributes: nil) }
            catch let error as NSError {
                print(error.localizedDescription);
        }
    }
    return (writePath as NSString).stringByAppendingPathComponent(filename)
}

//# MARK: - Save Image in Doc dir
func saveImage (image: UIImage, path: String ) -> Bool{

    let pngImageData = UIImagePNGRepresentation(image)
    //        let jpgImageData = UIImageJPEGRepresentation(image, 1.0)   // if you want to save as JPEG
    let result = pngImageData!.writeToFile(path, atomically: true)

    print("\(result)")
    print("\(path)")

    return result

}
Canner answered 5/10, 2015 at 11:45 Comment(0)
C
2

You can use URLByAppendingPathComponent() instead. Please note that you should trim the path string to remove “file://“ prefix:

let uniqueFileName = NSUUID().UUIDString
let documentsDirectory = getDocumentsDirectoryURL()
    if let path = documentsDirectory?.URLByAppendingPathComponent(uniqueFileName) {
        var pathString = path.absoluteString
        pathString = imagePathString.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "file://"))
}

func getDocumentsDirectoryURL() -> NSURL? {
    let fileManager = NSFileManager()
    if let docsDirectory = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first {
        return docsDirectory
    }
    return nil
}
Caladium answered 26/1, 2016 at 12:19 Comment(0)
L
1

I tried this and it solved the problem.

before:

let localPath = documentDirectory.URLByAppendingPathComponent(imageName)

after:

let localPath = (documentDirectory as NSString).appendingPathComponent(imageName)
Lorikeet answered 9/12, 2019 at 15:33 Comment(0)
S
0

Do the following:

(("\(fileName)" as NSString).lastPathComponent as NSString).stringByDeletingPathExtension
Suctorial answered 27/9, 2015 at 0:59 Comment(0)
N
-1

If using NSString path methods (instead of String URL methods) is acceptable, it's much easier to extend String with a computed property or a method returning its value as NSString (instead of duplicating the desired methods in String extension):

extension String
{
    var ns: NSString { return self as NSString }
}

and then:

swiftStringPath.ns.appendingPathComponent("whateva")
swiftStringPath.ns.deletingPathExtension
Neysa answered 17/3, 2019 at 20:12 Comment(0)
H
-3

Swift 4

extension String {

    var lastPathComponent: String {
        return (self as NSString).lastPathComponent
    }
    var pathExtension: String {
        return (self as NSString).pathExtension
    }
    var stringByDeletingLastPathComponent: String {
        return (self as NSString).deletingLastPathComponent
    }
    var stringByDeletingPathExtension: String {
        return (self as NSString).deletingPathExtension
    }
    var pathComponents: [String] {
        return (self as NSString).pathComponents
    }
    func stringByAppendingPathComponent(path: String) -> String {
        let nsSt = self as NSString
        return nsSt.appendingPathComponent(path)
    }
    func stringByAppendingPathExtension(ext: String) -> String? {
        let nsSt = self as NSString
        return nsSt.appendingPathExtension(ext)
    }
}
Hofstetter answered 8/6, 2018 at 11:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.