Search code examples
pythonpython-3.xswiftcocoadylib

How to use Swift dylib in Python Project in macOS?


I've simple Swift package created by using command

cd Desktop

mkdir AICore

cd AICore

swift package init --type library

and edited the configuration to dynamic library configuration like below,

Package.Swift

// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AICore",
    platforms: [.macOS(.v10_15)],
    
    products: [  
        .library(name: "AICore", type: .dynamic, targets: ["AICore"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "AICore",
            dependencies: [])
    ]
)

AICore.swift:

import Cocoa
 
public func launchMyAppKitWindow() {
    let app = NSApplication.shared
    let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300), styleMask: [.titled, .closable, .resizable], backing: .buffered, defer: false)
    window.center()
    window.title = "My AppKit Window"
    window.makeKeyAndOrderFront(nil)
    app.run()
}

Project Structure:

├── AICore
│   ├── Package.swift
│   ├── README.md
│   └── Sources
│       └── AICore
│           └── AICore.swift
└── Test
    ├── libAICore.dylib
    └── main.py

I've compiled my swift program using swift build and I've unhided the hidden build folder inside AICore parent folder. The library is locating at build->x86_64-apple-macosx->libAICore.dylib

I copied the library and pasted to Test folder which has below python script to test.

import ctypes
# https://docs.python.org/3/library/ctypes.html
# Load the dylib
my_appkit_lib = ctypes.CDLL("/Users/YxT2/Desktop/PY/Test/libAICore.dylib")

# Call the launch function
my_appkit_lib.launchMyAppKitWindow()

I run the code using the command python3 main.py and got below error

Traceback (most recent call last):
  File "/Users/YxT2/Desktop/PY/Test/main.py", line 7, in <module>
    my_appkit_lib.launchMyAppKitWindow()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ctypes/__init__.py", line 389, in __getattr__
    func = self.__getitem__(name)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ctypes/__init__.py", line 394, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: dlsym(0x21db45be0, launchMyAppKitWindow): symbol not found

I tested my swift code which is actually launching the window show in below screenshot.

enter image description here

But I want to use this test library to python code to call launchMyAppKitWindow function written in Swift, Can anyone help me to resolve this issue?


Solution

    1. Swift functions are not directly accessible from Python because Swift uses a different Application Binary Interface (ABI). To make the function accessible, you should mark it with the @_cdecl attribute to use the C ABI. Modify your AICore.swift like this:
        import Cocoa
        
        @_cdecl("launchMyAppKitWindow")
        public func launchMyAppKitWindow() {
          ...
        }
    
    1. Compile AICore.swift. Note: OS X "libAICore.dylib"
      # creates "libXXX.so" (or on OS X, "libAICore.dylib")
      swiftc -emit-library AICore.swift 
      
      
      
      

    enter image description here

    (Above image is taken from https://gist.github.com/jiaaro/e111f0f64d0cdb8aca38)

    1. The path in main.py - Check the library path.

      -- error in this line --

    my_appkit_lib = ctypes.CDLL("/Users/YxT2/Desktop/PY/Test/libAICore.dylib")