Search code examples
cswiftxcodecocoalanguage-interoperability

Setting file path in imported C function inside Swift Framework


I am trying to use C library source files inside my Cocoa Framework which has function named

void swe_set_ephe_path(char *path);

Which will basically be

swe_set_ephe_path(”C:\\SWEPH\\EPHE”);

for windows.

This library contains other data files which only work after this function is set. When imported to Swift the function looks like this

swe_set_ephe_path(path: UnsafeMutablePointer<Int8!>)

Since i want to bundle up all the data files in framework and use it in my application, i have done something like this

public class SwissEphemeris {
     public init() {
        let path = Bundle.main.bundlePath
        let swePath = UnsafeMutablePointer<Int8>(mutating: (path as NSString).utf8String)
        swe_set_ephe_path(swePath)
     }
}

But it seems it's not working and the functions which needs data to be searched in files are not able to operate.

Here is the picture for my directory structure

If anybody interested to look into Swiss library documentation, check here for the link, https://www.astro.com/swisseph/swephprg.htm#_Toc505244836


Solution

  • There are two problems:

    First, the resource files are in the “Resources” subdirectory of the framework, not in the top-level framework directory. You can obtain a path to that directory with

    let resourcePath = Bundle(identifier: "com.Abhi.SwissFramework")!.resourcePath!
    

    or with

    let resourcePath = Bundle(for: type(of: self)).resourcePath!
    

    I suggest to force-unwrap the optionals because you know that the bundle and the resources directory exist. A failure would indicate a build problem which should be detected early.

    Second, the C function takes a char * argument even though it does not mutate the passed string. Here you can use the approach from UnsafeMutablePointer<Int8> from String in Swift:

    resourcePath.withCString {
        swe_set_ephe_path(UnsafeMutablePointer(mutating: $0))
    }
    

    Even better: use the dedicated method withUnsafeFileSystemRepresentation() to get the file system representation of the resource path as a C string:

    let resourceURL = Bundle(for: type(of: self)).resourceURL!
    resourceURL.withUnsafeFileSystemRepresentation {
        swe_set_ephe_path(UnsafeMutablePointer(mutating: $0))
    }