Introducing…Python Stub Binaries for iOS Frameworks!
Wait, what are those you ask? Let me explain.
So basically, these stub binaries allow us to run Python code on an iOS device without having to go through the hassle of converting it into a native framework or using some fancy bridge technology. Instead, we can simply package our Python scripts as static libraries and link them directly into our Xcode project!
These stub binaries also allow us to use all the cool features of Objective-C and Swift in our Python code! Yes, you heard that right. We can now mix and match these languages like a true polyglot programmer!
So how do we get started with this magical technology? Well, let’s take a look at some examples:
1. First, create a new Xcode project using the “Single View App” template.
2. Add your Python script as a static library to your project by dragging and dropping it into the “Frameworks” folder or by adding it via the “Add Files to [Your Project]” menu option.
3. Link the Python framework to your main Xcode project using the “Link Binary With Libraries” build phase.
4. Add a new Objective-C class to your project and import the Python library like so:
// Import the Foundation framework to access its classes and methods
#import <Foundation/Foundation.h>
// Import the Python framework to access its classes and methods
#import <Python.h>
// Declare a new Objective-C class called "MyClass" that inherits from NSObject
@interface MyClass : NSObject
{
PyObject *pyModule; // Declare a variable to store our Python module object
}
// Declare a method called "runPythonScript" that takes no parameters and returns nothing
- (void)runPythonScript;
@end
// Implementation of the "MyClass" class
@implementation MyClass
// Implementation of the "runPythonScript" method
- (void)runPythonScript
{
// Import the Python library and store the module object in the "pyModule" variable
pyModule = PyImport_ImportModule("python_library");
// Check if the module was successfully imported
if (pyModule != NULL) {
// If successful, print a message to the console
NSLog(@"Python library imported successfully!");
} else {
// If unsuccessful, print an error message to the console
NSLog(@"Error importing Python library.");
}
}
@end
5. Implement the `runPythonScript()` method to load and execute your Python script:
- (void)runPythonScript {
// Load our Python module object using PyImport_AddModuleDict()
const char *moduleName = "my_python_script";
PyObject *pyModuleDict = PyDict_New(); // Create a new Python dictionary object to store our module
PyObject *pyFile = PyRun_String("import sys\n"
"sys.path.append('Frameworks')\n"
f"from %s import *", // Add the framework path to Python's sys.path
pyModuleDict, moduleName); // Load our Python script into the dictionary
PyObject *pyError = NULL;
PyErr_Fetch(&pyError, NULL, NULL); // Fetch any errors that occurred during script loading
if (PyErr_Occurred()) { // Check if an error occurred
NSLog(@"Failed to load %s: %s", moduleName, PyString_AsString(PyErr_Format(stderr, pyError))); // Log any errors that occurred during Python script loading
Py_DECREF(pyModuleDict); // Release the dictionary object
Py_XDECREF(pyFile); // Release the script object
return;
}
Py_DECREF(pyFile); // Release the script object
// Load our Python module object using PyImport_AddModule()
pyModule = PyImport_AddModule(moduleName, NULL); // Load our Python module using the dictionary
if (NULL == pyModule) { // Check if the module was loaded successfully
NSLog(@"Failed to load %s: %s", moduleName, PyString_AsString(PyErr_Format(stderr, PyErr_Occurred()))); // Log any errors that occurred during Python script loading
return;
}
// Call our main function in the loaded Python module object using PyObject_CallFunction()
PyObject *pyArgs = NULL; // Create an empty argument list
PyObject *pyResult = PyObject_CallFunction(PyString_FromString("main"), pyArgs); // Call the main function in our module with the empty argument list
if (NULL == pyResult) { // Check if an error occurred during execution
NSLog(@"Failed to execute %s: %s", moduleName, PyString_AsString(PyErr_Format(stderr, PyErr_Occurred()))); // Log any errors that occurred during Python script execution
return;
}
// Clean up our resources and exit gracefully
Py_DECREF(pyArgs); // Release the argument list
Py_XDECREF(pyResult); // Release the result object
}
6. Run your Xcode project on an iOS device or simulator to see the magic in action!
And that’s it, You now have a fully functional Python stub binary for iOS frameworks!
This technology is still in its infancy and we can expect to see some exciting developments in the near future. For example:
– Support for Swift modules instead of Objective-C classes (this would allow us to use Python code directly from our Swift code)
– Integration with popular Python frameworks like NumPy, Pandas, and Scikit-Learn
– Improved performance through the use of JIT compilers or bytecode interpreters
So stay tuned for more exciting developments in this space!
So what are you waiting for? Go ahead and give it a try! And if you have any questions or feedback, feel free to reach out to me on Twitter @Python_iOS.