or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

categories.mdcore-bridge.mddecorators.mdframework-loading.mdindex.mdprotocols.mdtype-system.mdutilities.md

categories.mddocs/

0

# Categories and Method Addition

1

2

Category support for extending existing Objective-C classes with new methods. Categories allow adding functionality to existing classes without subclassing or modifying the original class definition, providing a powerful mechanism for extending system frameworks and third-party classes.

3

4

## Capabilities

5

6

### Class Method Addition

7

8

Functions for dynamically adding methods to existing Objective-C classes.

9

10

```python { .api }

11

def classAddMethod(targetClass, method, targetMethod):

12

"""

13

Add a method to an existing Objective-C class.

14

15

Args:

16

targetClass: The Objective-C class to modify

17

method: The method selector to add

18

targetMethod: The implementation function to add

19

20

Usage:

21

def new_method_impl(self, _cmd):

22

return "Hello from new method!"

23

24

objc.classAddMethod(

25

NSString,

26

"newMethod",

27

new_method_impl

28

)

29

"""

30

```

31

32

### Category Decorator

33

34

Decorator for creating Objective-C categories from Python classes.

35

36

```python { .api }

37

def category(baseClass):

38

"""

39

Decorator to create an Objective-C category from a Python class.

40

41

Args:

42

baseClass: The Objective-C class to extend with category methods

43

44

Returns:

45

Decorator function that creates the category

46

47

Usage:

48

@objc.category(NSString)

49

class NSStringExtensions:

50

def isPythonic(self):

51

return self.hasPrefix_("py") or self.hasSuffix_(".py")

52

53

@objc.signature(b'@@:@')

54

def stringByAppendingPythonSuffix_(self, suffix):

55

return self.stringByAppendingFormat_("_py_%@", suffix)

56

"""

57

```

58

59

### Category Implementation

60

61

Core category implementation class for managing method injection.

62

63

```python { .api }

64

class Category:

65

"""

66

Core implementation class for Objective-C categories.

67

68

Manages the process of injecting methods from Python classes

69

into existing Objective-C classes, handling method signatures,

70

type conversion, and proper integration with the Objective-C runtime.

71

"""

72

73

def __init__(self, baseClass):

74

"""

75

Initialize a category for the specified base class.

76

77

Args:

78

baseClass: The Objective-C class to extend

79

"""

80

81

def addMethod(self, method_name: str, implementation):

82

"""

83

Add a method to the category.

84

85

Args:

86

method_name (str): Name of the method to add

87

implementation: Python function implementing the method

88

"""

89

90

def inject(self):

91

"""

92

Inject all category methods into the target class.

93

94

This method performs the actual modification of the Objective-C

95

class, adding all methods defined in the category.

96

"""

97

```

98

99

## Usage Examples

100

101

### Adding Methods to NSString

102

103

```python

104

import objc

105

from Foundation import NSString

106

107

@objc.category(NSString)

108

class NSStringPythonExtensions:

109

110

def isPythonic(self):

111

"""Check if string contains Python-related keywords."""

112

return (self.hasPrefix_("py") or

113

self.hasSuffix_(".py") or

114

self.containsString_("python"))

115

116

@objc.signature(b'@@:@')

117

def stringByAppendingPythonSuffix_(self, suffix):

118

"""Append a Python-style suffix to the string."""

119

return self.stringByAppendingFormat_("_py_%@", suffix)

120

121

@objc.signature(b'@@:')

122

def pythonize(self):

123

"""Convert the string to a Python-friendly format."""

124

return self.lowercaseString().stringByReplacingOccurrencesOfString_withString_(

125

" ", "_"

126

)

127

128

# Usage after category definition

129

test_string = NSString.stringWithString_("Hello World")

130

print(test_string.isPythonic()) # False

131

print(test_string.pythonize()) # "hello_world"

132

133

py_string = NSString.stringWithString_("python_script.py")

134

print(py_string.isPythonic()) # True

135

print(py_string.stringByAppendingPythonSuffix_("backup")) # "python_script.py_py_backup"

136

```

137

138

### Adding Methods to NSArray

139

140

```python

141

import objc

142

from Foundation import NSArray, NSMutableArray

143

144

@objc.category(NSArray)

145

class NSArrayPythonExtensions:

146

147

def pythonList(self):

148

"""Convert NSArray to Python list."""

149

result = []

150

for i in range(self.count()):

151

result.append(self.objectAtIndex_(i))

152

return result

153

154

@objc.signature(b'@@:@')

155

def arrayByApplyingBlock_(self, block):

156

"""Apply a block to each element and return new array."""

157

result = NSMutableArray.alloc().init()

158

for i in range(self.count()):

159

item = self.objectAtIndex_(i)

160

transformed = block(item)

161

result.addObject_(transformed)

162

return result.copy()

163

164

def isEmpty(self):

165

"""Check if array is empty."""

166

return self.count() == 0

167

168

# Usage

169

array = NSArray.arrayWithObjects_("apple", "banana", "cherry", None)

170

print(array.pythonList()) # ['apple', 'banana', 'cherry']

171

print(array.isEmpty()) # False

172

173

# Transform array elements

174

upper_array = array.arrayByApplyingBlock_(lambda x: x.uppercaseString())

175

print(upper_array.pythonList()) # ['APPLE', 'BANANA', 'CHERRY']

176

```

177

178

### Adding Methods to Custom Classes

179

180

```python

181

import objc

182

from Foundation import NSObject

183

184

# Define a simple custom class

185

class MyCustomClass(NSObject):

186

def init(self):

187

self = objc.super(MyCustomClass, self).init()

188

if self is None:

189

return None

190

self._data = []

191

return self

192

193

# Create category to extend it

194

@objc.category(MyCustomClass)

195

class MyCustomClassExtensions:

196

197

def addItem_(self, item):

198

"""Add an item to the internal data storage."""

199

self._data.append(item)

200

201

def getItemCount(self):

202

"""Get the number of items stored."""

203

return len(self._data)

204

205

@objc.signature(b'@@:i')

206

def getItemAtIndex_(self, index):

207

"""Get item at specific index."""

208

if 0 <= index < len(self._data):

209

return self._data[index]

210

return None

211

212

def clearAllItems(self):

213

"""Remove all items from storage."""

214

self._data.clear()

215

216

# Usage

217

obj = MyCustomClass.alloc().init()

218

obj.addItem_("First item")

219

obj.addItem_("Second item")

220

print(obj.getItemCount()) # 2

221

print(obj.getItemAtIndex_(0)) # "First item"

222

obj.clearAllItems()

223

print(obj.getItemCount()) # 0

224

```

225

226

### Low-level Method Addition

227

228

```python

229

import objc

230

from Foundation import NSString

231

232

def custom_reverse_method(self, _cmd):

233

"""Custom method implementation that reverses the string."""

234

# Get the string content

235

original = str(self)

236

# Reverse it

237

reversed_str = original[::-1]

238

# Return as NSString

239

return NSString.stringWithString_(reversed_str)

240

241

# Add method directly to NSString class

242

objc.classAddMethod(

243

NSString,

244

"reverseString",

245

custom_reverse_method

246

)

247

248

# Usage

249

test_string = NSString.stringWithString_("Hello World")

250

reversed_string = test_string.reverseString()

251

print(reversed_string) # "dlroW olleH"

252

```

253

254

### Category with Complex Method Signatures

255

256

```python

257

import objc

258

from Foundation import NSString, NSArray

259

260

@objc.category(NSString)

261

class NSStringAdvancedExtensions:

262

263

@objc.signature(b'@@:@@i')

264

def stringByReplacingRange_withString_options_(self, range_dict, replacement, options):

265

"""

266

Replace a range of characters with new string and options.

267

268

Args:

269

range_dict: Dictionary with 'location' and 'length' keys

270

replacement: String to insert

271

options: Replacement options

272

"""

273

location = range_dict.get('location', 0)

274

length = range_dict.get('length', 0)

275

276

# Create NSRange equivalent

277

before = self.substringToIndex_(location)

278

after = self.substringFromIndex_(location + length)

279

280

return before.stringByAppendingString_(

281

replacement.stringByAppendingString_(after)

282

)

283

284

@objc.signature(b'@@:@')

285

def componentsSeparatedByMultipleDelimiters_(self, delimiters_array):

286

"""

287

Split string by multiple delimiters.

288

289

Args:

290

delimiters_array: NSArray of delimiter strings

291

"""

292

result = NSArray.arrayWithObject_(self)

293

294

for i in range(delimiters_array.count()):

295

delimiter = delimiters_array.objectAtIndex_(i)

296

new_result = []

297

298

for j in range(result.count()):

299

string_part = result.objectAtIndex_(j)

300

components = string_part.componentsSeparatedByString_(delimiter)

301

for k in range(components.count()):

302

component = components.objectAtIndex_(k)

303

if component.length() > 0:

304

new_result.append(component)

305

306

result = NSArray.arrayWithArray_(new_result)

307

308

return result

309

310

# Usage

311

text = NSString.stringWithString_("apple,banana;cherry:date")

312

delimiters = NSArray.arrayWithObjects_(",", ";", ":", None)

313

components = text.componentsSeparatedByMultipleDelimiters_(delimiters)

314

315

print("Components:")

316

for i in range(components.count()):

317

print(f" {components.objectAtIndex_(i)}")

318

# Output:

319

# apple

320

# banana

321

# cherry

322

# date

323

```

324

325

## Best Practices

326

327

### Method Naming

328

329

- Follow Objective-C naming conventions (camelCase)

330

- Use descriptive names that indicate the method's purpose

331

- For methods that take parameters, end with an underscore

332

333

### Type Signatures

334

335

- Always provide type signatures for methods with complex parameters

336

- Use `@objc.signature()` decorator for explicit type information

337

- Test method signatures thoroughly to ensure proper bridge behavior

338

339

### Memory Management

340

341

- Categories inherit the memory management rules of their target class

342

- Be careful with object retention and autorelease in category methods

343

- Use `objc.autorelease_pool()` for methods that create many temporary objects

344

345

### Error Handling

346

347

- Categories should handle errors gracefully

348

- Don't assume the target class's internal state

349

- Validate parameters before using them in category methods