140

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.

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
Maysam
  • 7,246
  • 13
  • 68
  • 106

11 Answers11

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")
Niklas Berglund
  • 3,563
  • 4
  • 32
  • 32
Dániel Nagy
  • 11,815
  • 9
  • 50
  • 58
  • if you are going to use this design that you will have problems like convert space to %20 `Application%20Support` – Roman Sep 19 '15 at 00:15
  • no, Swift 2.0 can use `stringByAppendingPathComponent`, see my answer below. – Jeffrey Neo Oct 05 '15 at 12:23
  • 3
    @JeffreyNeo yes, but that is not a `NSURL` method but an `NSString` – Dániel Nagy Oct 05 '15 at 12:38
  • @DánielNagy I means you said "`stringByAppendingPathComponent` is removed in Swift 2.0" is not correct, and @Maysam didn't ask for only `NSURL` method. – Jeffrey Neo Oct 05 '15 at 16:07
  • 5
    @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. – Dániel Nagy Oct 05 '15 at 16:44
  • @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: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/ and https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/ – Jeffrey Neo Oct 05 '15 at 17:58
  • @JeffreyNeo yes, they are perfectly acceptable, I just wanted to make a point that the method `stringByAppendingPathComponent` was removed from Swift 2.0 – Dániel Nagy Oct 05 '15 at 18:19
  • It's funny that NSTemporaryDirectory() should return "String" but it's a NSURL actually. Anyone can explain this? – allenlinli May 16 '16 at 06:34
  • @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`? – Dániel Nagy May 17 '16 at 07:34
  • @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. – allenlinli May 17 '16 at 15:47
  • @AllenLin but first you create an `NSURL` with `NSURL(fileURLWithPath: NSTemporaryDirectory())`, and that has the `URLByAppendingPathComponent`, not the `String`. – Dániel Nagy May 18 '16 at 08:15
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)
    }
}
Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
  • 12
    While this is a valid solution, there is a reason why Apple has removed those methods - using paths to locating resources is deprecated and `NSURL`s should be used instead. Just saying. – Charlie Monroe Jan 20 '16 at 14:32
  • snippet: String( NSString( string: path ).stringByAppendingPathComponent( imageName ) ) ... otherwise, quite agreed with @CharlieMonroe – Bobjt Mar 20 '16 at 19:48
  • 1
    @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? – Joris Mans Jan 07 '17 at 20:20
  • @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)? – Charlie Monroe Jan 08 '17 at 07:41
  • 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. https://github.com/apple/swift/blob/adc54c8a4d13fbebfeb68244bac401ef2528d6d0/stdlib/public/SDK/Foundation/NSStringAPI.swift#L1345 – Brennan Mar 10 '17 at 17:29
  • @CharlieMonroe, Then why isn't there a file exists at url method on `FileManager`? – Iulian Onofrei Sep 22 '19 at 17:33
  • 1
    @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. – Charlie Monroe Sep 24 '19 at 04:36
  • @CharlieMonroe Too bad that these replacements are not mentioned in the docs. – Iulian Onofrei Sep 24 '19 at 05:22
30

Simply wrap your string as NSString.

let writePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("instagram.igo")
Jeffrey Neo
  • 3,693
  • 2
  • 26
  • 30
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)
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
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
Revanth
  • 273
  • 1
  • 5
  • 10
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

}
Mehul Chuahan
  • 752
  • 8
  • 19
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
}
Inbal Tish
  • 257
  • 3
  • 5
0

Do the following:

(("\(fileName)" as NSString).lastPathComponent as NSString).stringByDeletingPathExtension
spenibus
  • 4,339
  • 11
  • 26
  • 35
0

I tried this and it solved the problem.

before:

let localPath = documentDirectory.URLByAppendingPathComponent(imageName)

after:

let localPath = (documentDirectory as NSString).appendingPathComponent(imageName)
Nils
  • 910
  • 8
  • 30
Miah G.
  • 129
  • 1
  • 7
-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
Russian
  • 1,296
  • 10
  • 15
-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)
    }
}
Duncan Groenewald
  • 8,496
  • 6
  • 41
  • 76