skip to Main Content

I am working on a C++ library that shall do image processing. My approach now is to pass two strings to C++ from swift. One string is the path to the image and the second is the output directory.

All the time I get that the file does not exist. How can I get the correct path the asset? The image lies in a directory I have created on my own and is called "test.jpg". I also have a "test2.jpg" in Assets. Have not managed to find that either.

Swift Code:

func getGrayImage() -> Image {
    if var resourcePath = Bundle.main.url(forResource: "test", withExtension: "jpg") {
        let dir = resourcePath.deletingLastPathComponent()
        VideoProcessingWrapper().rgb2gray(resourcePath.absoluteString, dir.absoluteString)
    }
    return Image("test2")
}

C++ Code:

void VideoProcessing::rgb2gray(const std::string& image_path, const std::string& dir) {
  std::cout << "C++: " << std::endl;
  std::cout << image_path << std::endl;
  std::cout << std::endl;
  std::cout << dir << std::endl;
  std::cout << "In directory: " <<std::endl;
  for (const auto& entry : std::filesystem::directory_iterator(dir)) {
        std::cout << entry.path() << std::endl;
  }
  if (std::filesystem::exists(image_path)) {
    std::cout << "File exists" << std::endl;
  } else {
    std::cout << "File does NOT exist" << std::endl;
    return;
  }
  cv::imread(image_path, cv::IMREAD_COLOR);
}

It even crashes when trying to display all files in the directory, stating that the directory does not exist. But what have I then been given from Bundle.main.url-call?

This is the printout from the C++ function using a simulator:

C++: 
file:///Users/name/Library/Developer/CoreSimulator/Devices/7F438661-8BF5-4A60-B41F-1D4B7FEC6A8E/data/Containers/Bundle/Application/AB3EFF3E-79A4-469C-A3BF-ABDD31A09E61/TestVideoProcess.app/test.jpg

file:///Users/name/Library/Developer/CoreSimulator/Devices/7F438661-8BF5-4A60-B41F-1D4B7FEC6A8E/data/Containers/Bundle/Application/AB3EFF3E-79A4-469C-A3BF-ABDD31A09E61/TestVideoProcess.app/

I get the same behaviour when running on a real iPhone.

2

Answers


  1. The problem here is that Foundation framework represents paths with URI‘s, while C++ standard library filesystem relies on more file-path-specific format which may consist of only the following components:

    1. root-name(optional): identifies the root on a filesystem with multiple roots (such as "C:" or "//myserver"). In case of
      ambiguity, the longest sequence of characters that forms a valid
      root-name is treated as the root-name. The standard library may define
      additional root-names besides the ones understood by the OS API.
    2. root-directory(optional): a directory separator that, if present, marks this path as absolute. If it is missing (and the first
      element other than the root name is a file name), then the path is
      relative and requires another path as the starting location to resolve
      to a file name.
    3. Zero or more of the following:
    • file-name: sequence of characters that aren’t directory separators or preferred directory separators (additional limitations
      may be imposed by the OS or file system). This name may identify a
      file, a hard link, a symbolic link, or a directory. Two special
      file-names are recognized:
    • dot: the file name consisting of a single dot character . is a directory name that refers to the current directory dot-dot: the file
      name consisting of two dot characters .. is a directory name that
      refers to the parent directory.
    • directory-separators: the forward slash character / or the alternative character provided as path::preferred_separator. If this
      character is repeated, it is treated as a single directory separator:
      /usr///////lib is the same as /usr/lib

    A URI consists of quite a lot of different parts:
    enter image description here

    In this case you need the path part only. One trick you can employ here is to build the path from pathComponents of the URL instance:

    if let resourcePath = Bundle.main.url(forResource: "test", withExtension: "jpg") {
        let dir = resourcePath.deletingLastPathComponent()
        VideoProcessingWrapper().rgb2gray(resourcePath.pathComponents.joined(separator: "/"),
                                          dir.pathComponents.joined(separator: "/"))
    
    }
    

    Alternatively, as suggested by mani in the comments to your question, you can use the path property of the same class, however the documentation says that it also may include parameter string and for some reason it’s now deprecated.

    Login or Signup to reply.
  2. No matter what you do, the bundle directory is inside your application and you have no write access to it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search