nottombrown
nottombrown

Reputation: 535

Locate source project directory in Swift from compiled code

I want to find the directory of my uncompiled source code (because I want to count the lines of it) in my own swift project.

let projectRepo = "/Users/tombrown/Workspace/SwiftGolf" // <-- I want this programmatically
let path = "\(projectRepo)/SwiftGolf/Golf.swift"

var fileContents = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)

I've found the compiled location with NSBundle.mainBundle().bundlePath, but it doesn't contain the uncompiled code. Any thoughts? Maybe there's a way that I could write a temporary file during the build process?

Upvotes: 3

Views: 5388

Answers (2)

johndpope
johndpope

Reputation: 5249

The challenge is getting swift to infer location at runtime vs compile time. The arguments in xcode suggestion works well and can be accepted as solution. I couldn't use this approach because I'm using SPM without xcode. Another approach could be to use this library https://github.com/JohnSundell/Files

in you Package.swift

dependencies: [
          .Package(url: "https://github.com/JohnSundell/Files.git", Version(1,8,0))
    ]

then in your code

import Files
let projectRepo = "\(Folder.home.path)/gitWorkspace/SwiftGolf"
do {
 let model = try Data(contentsOf:URL(fileURLWithPath: "\(projectDir)/\(modelFile)"))

}catch {

   print("error: \(error)")

}

another option is CommandLineKit / xcode arguments

import CommandLineKit .  
let cmdLine = CommandLineKit.CommandLine() .    
let file = StringOption(shortFlag: "f",   
                    longFlag: "file",    
                    required: true,   
                    helpMessage: "The file you're trying to parse") .   
cmdLine.addOptions(file)


do {
    try cmdLine.parse()
    print(file.value!)   
} catch {
    print("error: \(error)")
    exit(-1)
}

enter image description here

Upvotes: 2

zubko
zubko

Reputation: 1797

As adding source files to Build Phases > Copy Bundle Resources doesn't work and to have more flexibility, I recommend to use a little tiny bit of Objective-C.

In the project's Build Settings add the following line:

steps to add a macro

and then if your project already has a bridging header the const will become available automatically from Swift

do {
    let path = "\(PROJECT_DIR)/Test/ViewController.swift"
    let source = try String(contentsOfFile: path)
    print(source)
} catch {
    print("Error handling goes here")
}

If the project doesn't have a bridging header then it's possible to make Xcode to add one by adding a new Objective-C class to the project and then Xcode will ask about the bridging header.

EDIT:

Also of course bridging header can be added manually, by adding a new header file to the project, and then setting its path to the "Objective-C Bridging Header" in project's Build Settings.

Usually the PROJECT_DIR macro will not be available to Swift code automatically, in the bridging header add:

@import Foundation;

static const NSString *kProjectDir = PROJECT_DIR;

END EDIT


If you are fine with readonly access to the files, you can make Xcode to copy the source files into the app's resource folder by adding a custom script or a Copy Files Build Phase like on the image:

steps to add copy files phase

Then the path to the file will be:

let path = NSBundle.mainBundle().pathForResource("ViewController", ofType: "swift")

Upvotes: 1

Related Questions