Skip to content

Swift Integration - Basics

This section introduces Swift integration for Kivy on iOS. You will learn how to expose Swift modules, classes, properties and methods to Python, eventually enabling you to access native iOS APIs directly from your Kivy app.

Why Integrate Swift with Kivy?

The PySwiftKit toolchain allows your Kivy/Python code to interact with Swift, which is the primary language for modern iOS development. This means you can:

  • Access any iOS API available to Swift
  • Extend your app with features not accessible from Python alone
  • Write performance-critical or platform-specific code in Swift

Before you begin, no prior Swift knowledge is required. This guide covers the basics you need to get started.

Swift Essentials for Kivy Developers

A brief primer on Swift, focused only on what you need for integration:

  • Swift files have a .swift extension and live inside the Sources folder of your Xcode project.
  • A Swift class is defined using the class keyword and can contain properties (declared with var) and methods (declared with func).
  • Initializers (init) are special methods that run when the class is created, similar to def __init__ in Python.

Example: Minimal Swift class

HelloWorld.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class HelloWorld {
    var name: String

    init() {
        self.name = "Kivy"
        print("Hello, \(self.name)")
    }

    func greet() {
        print("Hello from Swift!")
    }
}

Exposing a Swift module to Python

You will need to do this only once. This process involves creating a Swift file, defining a module, and registering it in your Kivy project. After this setup, you can easily add more Swift classes and methods as needed.

Let's create a simple Swift module called auth that can be imported from Python.

1. Create a Swift File

In Xcode, right-click the Sources folder, select New Empty File, and name it Auth.swift.

2. Import Required Modules

Add these imports at the top:

Auth.swift
1
2
3
4
5
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper

3. Define and Expose a Python Module

Add the following code to define and expose a minimal Python module from Swift:

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        // Classes will be listed here later
    ]
}

The @PyModule decorator tells PySwiftKit that Auth is going to become Python module. The py_classes array will hold any Swift classes you want to expose to Python.

4. Register the Module in Main.swift

Open Main.swift and update the import list:

Main.swift
1
2
3
4
5
6
7
8
import Foundation
import KivyLauncher
import PySwiftObject


let pythonSwiftImportList: [PySwiftModuleImport] = [
    .init(name: "auth", module: Auth.py_init)
]

With this, your Swift code creates a Python module named auth. You could name it anything you like. Even without any classes registered yet, you can already import this empty module in Python:

import auth
print(auth)

This verifies the integration and confirms the Swift module is accessible from Python. Classes and methods will be added in later steps.

Minimal Python example:

main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from kivy.app import App
from kivy.lang import Builder

kv = """
Button:
    text: "import module auth"
    on_release: app.test_import()
"""


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)

    def test_import(self):
        import auth

        print(auth)


app = MainApp()
app.run()

Exposing Swift Module

As you can see, the auth module is successfully imported and printed in the console.

Exposing a Swift Class to Python

1. Define a Swift Class

Go back to your Auth.swift file and define a Swift class that you want to expose to Python. Let's create a simple class called User.

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


class User {}


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        // Classes will be listed here later
    ]
}

2. Expose the Swift Class to Python

To expose the User class to Python, you need to decorate it with @PyClass. This tells PySwiftKit to make this class available in Python.

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


@PyClass
class User {}


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        // Classes will be listed here later
    ]
}

3. Register the Swift Class in the Module

Now, you need to register the User class in the Auth module. Update the py_classes array to include your class:

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


@PyClass
class User {}


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        User.self
    ]
}

4. Use the Swift Class in Python

Now you can use the User class in your Python code. Open your Kivy app's main file (e.g., main.py) and add the following:

main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import auth
from kivy.app import App
from kivy.lang import Builder

kv = """
Button:
    text: "print User class"
    on_release: app.print_user()
"""


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)

    def print_user(self):
        user = auth.User
        print(user)  # This will print the User object in the Xcode console


app = MainApp()
app.run()

Exposing Swift Class

Exposing Swift Initializers and Properties to Python

Before you can create instances or access properties from Python, you need to expose initializers and properties in your Swift class. Now you'll see how to make the User class instantiable and how to access its attributes.

1. Add an Initializer to the Swift Class

By default, Python won't know how to instantiate the Swift class unless you expose the initializer. If you currently try to instantiate the User class without an initializer, you will get an error like this:

NotImplementedError: User can only be inited from swift

Create an initializer in your User class and decorate it with @PyInit:

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


@PyClass
class User {
    @PyProperty
    var name: String

    @PyInit
    init(name: String) {
        self.name = name
    }
}


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        User.self
    ]
}
Here we added a property name decorated with @PyProperty, which allows Python to access and modify this property.

Also, we created an initializer (init) that takes a name parameter (of type String). The @PyInit decorator tells PySwiftKit that this is the initializer that Python will call when creating an instance of the User class.

Now you can instantiate the User class from Python like this:

main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import auth
from kivy.app import App
from kivy.lang import Builder

kv = """
Button:
    text: "Create User"
    on_release: app.create_user()
"""


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)

    def create_user(self):
        user = auth.User("Kivy School")
        print(user.name)  # This will print "Kivy School" in the Xcode console


app = MainApp()
app.run()

Exposing Swift Init

Exposing Swift Methods to Python

To expose methods in your Swift class, you need to decorate them with @PyMethod. This allows Python to call these methods as if they were regular Python methods.

1. Add a Method to the Swift Class

Create a method in your User class and decorate it with @PyMethod:

Auth.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import Foundation
import PySwiftKit
import PySerializing
import PySwiftObject
import PySwiftWrapper


@PyClass
class User {
    @PyProperty
    var name: String = "Test"

    @PyInit
    init(name: String) {
        self.name = name
    }

    @PyMethod
    func greet() {
        print("Hello, \(self.name)!")
    }
}


@PyModule
struct Auth: PyModuleProtocol {
    static var py_classes: [any (PyClassProtocol & AnyObject).Type] = [
        User.self
    ]
}

Now you can call the greet method from Python:

main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import auth
from kivy.app import App
from kivy.lang import Builder

kv = """
Button:
    text: "Greet User"
    on_release: app.greet_user()
"""


class MainApp(App):
    def build(self):
        return Builder.load_string(kv)

    def greet_user(self):
        user = auth.User("Kivy School")
        user.greet()  # Prints: Hello, Kivy School!


app = MainApp()
app.run()

Exposing Swift Method

Summary of Key Decorators

  • @PyModule defines a Python module that can be imported in Python.
  • @PyClass tells PySwiftKit to expose this class to Python.
  • @PyProperty exposes properties to Python, allowing access and modification.
  • @PyInit marks the initializer, allowing Python to create instances of the class.
  • @PyMethod exposes methods to Python, allowing them to be called as if they were regular Python methods.

Recap

  • You created a Swift module that can be imported in Python.
  • You created a Swift class and registered the class in a Swift module and made it available for Python import.
  • You added an initializer to the Swift class, allowing Python to create instances.
  • You added properties to the Swift class, allowing Python to access and modify them.
  • You added methods to the Swift class, allowing Python to call them directly.

This is the simplest form of Swift integration. Once you are comfortable with this process, you can build more complex interactions, including passing data, using Swift for custom UI, or calling back from Swift to Python.


Next: Explore more advanced integration patterns and real-world examples. (in development)