An alternative, single-header and purely C-focused wrapper to Apple's Cocoa API for MacOS app development. Requires little to no Objective-C knowledge to use.
Because there isn't anything else like it (to my knowledge, at least). If you want to create low-level MacOS apps, you have to learn how to use Objective-C effectively, and as we all know the language is notorious for its syntax and its unique (albeit terrible) way of handling object-oriented programming. It also ruins cross-platform code as if you compile an Objective-C file as C, you get a bunch of errors (however somewhat sophisticated build system help that).
A lot of people to that would say "Just use swift instead". While Swift does provide a better syntax than Objective-C (not in all cases), some issues still persist. Swift, much like Objective-C, is still only really used for Apple development and nothing else. It's not really worth learning an even more radically different language to create low-level applications (and personally speaking I don't really care for Swift). The biggest flunder when it comes to cross-platform development would have to be the fact that Swift is very hard to integrate in non Apple-oriented projects, which is a far cry from Objective-C considering it's just C with sugar syntax. Either way, Objective-C prevails in this department.
Apart from Objective-C/Swift, you can also use 3rd-party libraries that obfuscate this whole issue for you. While fine for more higher-level projects, for developers that need total low-level control to satisfy their needs, this isn't suffice. There is also hidefromkgb's mac_load, which allows for actual Mac development in "pure" C. While I do like the project (and have used some of its code in this codebase), there's a lot left to be desired. Using it feels like you're still writing Objective-C code in C with how you need to define your own classes constantly and using programming concepts only found in Objective-C. Due to this much of the written code also looks quite ugly and quite unsensible comparing to the original Objective-C counterpart. It's also not being maintained anymore.
Silicon provides a full C functional-oriented programming wrapper over Cocoa for those low-level programmers in need, as well as anyone that doesn't really want to learn Objective-C or Swift. This library provides a full suite of functions, types, macros, enums, etc from the Cocoa to make a C-syntax friendly library.
As with any young FOSS project, Silicon is subjected to major change in the future and as such massive overhauls should be expected.
Silicon is also very unfinished as a complete implementation of a giant library like Cocoa will take awhile to achieve. As of now, Silicon will firstly focus on implementing the Cocoa essentials, plus common code examples and functions used in the API.
As of Aprill 11th of 2023, it's possible to compile basic C code with Silicon's Makefile
. While GUI support is nowhere near implemented, it's already possible to print something in the terminal with the print.c example.
- Xcode:
- iOS SDK.
- iOS simulator.
- Silicon's
Makefile
. - Setting said
Makefile
'sTARGET_iOS
to true at line 6.
To build, install and launch the app all at once, you'll have to run the command make iosBuild
. This'll first compile the source file, then generate an .app
for it, install it on the currently opened iOS simulator and launch it with a console terminal.
If you want to set the icon, you have to either run make iosBuild ICON=<filename>.png
, or make generateApp ICON=<filename>.png
to only generate the app with the icon.
If you only want to install or launch the app, then you can use the command make iosInstall
and make iosLaunch
respectively.
If Silicon does not include a function from Cocoa that you need, you'll have to either create an issue on the github repo asking for the function or try to create your own function.
(These steps don't have to be done in order per se)\
On line ~127 there is a bunch of typedefs for class-types\
If you can't find your class type simply add it to appropriate somewhere on the list
eg. `NSWindow` becomes `typedef void NSWindow;`
`SICDEF [return-type] [class]_([object (if needed)], [args]);`\
```c
ex.
[window setFrame:(frame) display:(true) animate:(true)];
becomes
void NSWindow_setFrameAndDisplay(NSWindow* window, NSRect frame, bool display, bool animate)
[NSFont fontWithName:(str) size:(fontSize)];
becomes
NSFont* NSFont_init(const char* fontName, CGFloat fontSize);
```
If your function is init'ing an object from a class, you may also need to define the class\
In which case, first check if the class is already defined, you can do this by checking the enum on line ~1239\
It will be in all caps in this format (NS_[CLASS]_CODE), eg. NSWindow becomes `NS_WINDOW_CODE`\
If it's not defined, find a proper place to put it in the enum\
Then go to the `si_initNS` function where you will define the class in the `SI_NS_CLASSES` array,\
if the `SI_NS_CLASSES` array is too small, raise it by 1\
eg. `void* SI_NS_CLASSES[36] = {NULL};` to `void* SI_NS_CLASSES[37] = {NULL};`\
Then find a proper place in the function to define the class and define it like this\
`SI_NS_CLASSES[(CLASS ENUM)] = objc_getClass("[CLASS NAME]");`
for example,
```c
SI_NS_CLASSES[NS_WINDOW_CODE] = obj_getClass("NSWindow");
```
First, check if the function is already defined, you can do this by checking the enum on line ~1239\
It will be in all caps in this format (NS_[CLASS_NAME]_[FUNCTION_NAME]_CODE)\
eg. NSWindow_makeMainWindow becomes `NS_WINDOW_MAKE_MAIN_WINDOW_CODE`\
If it's not defined, find a proper place to put it in the enum\
Then go to the `si_initNS` function where you will define the class in the `SI_NS_FUNCTIONS` array,\
if the `SI_NS_FUNCTIONS` array is too small, raise it by 1\
eg. `void* SI_NS_FUNCTIONS[232];` to `void* SI_NS_CLASSES[233];`\
Then find a proper place in the function to define the class and define it like this\
`SI_NS_FUNCTIONS[(CLASS ENUM)] = objc_getClass("[FUNCTION NAME]");`
for example,
```c
SI_NS_FUNCTIONS[NS_WINDOW_MAKE_MAIN_WINDOW_CODE] = obj_getClass("makeMainWindow");
```
If your function has args, the class name (for objc_getClass) must include them\
If it's only one arg, you just need to add `:` at the end, if it's more than one,
you add one `:` then a `arg_name:` for each arg after
eg.
```c
NSWindow_setFrameAndDisplay(NSWindow* window, NSRect frame, bool display, bool animate);
becomes
SI_NS_FUNCTIONS[NS_WINDOW_SET_FRAME_AND_DISPLAY_CODE] = sel_getUid("setFrame:display:animate:");
```
Check the Cocoa [documentation](https://developer.apple.com/documentation/) for more information
Simply find a good spot to define the function under the `#ifdef SILICON_IMPLEMENTATION` line\
Here is a quick example for `makeMainWindow`\
First, define `void* func` as being the function selector you want to use\
`void* func = SI_NS_FUNCTIONS[NS_WINDOW_MAKE_MAIN_WINDOW_CODE];`\
Next, use the proper `objc_msgSend` depending on the function's input and return type\
you may have to cast it yourself if none is avaliable, I'd suggest you check out functions which do this first.\
For `makeMainWindow`, we can use `objc_msgSend_void`
```c
void NSWindow_makeMainWindow(NSWindow* window) {
void* func = SI_NS_FUNCTIONS[NS_WINDOW_MAKE_MAIN_WINDOW_CODE];
objc_msgSend_void(window, func);
}
```
You may also need to convert NSArray to siArray (or vice versa), or NSString to const char* (or vice versa)\
In such cases I would suggest you refference other functions that do this
When doing a `_init` function, you may need to do an allocation for the class.\
Here is an example of that
```c
NSButton* NSButton_initWithFrame(NSRect frameRect) {
void* func = SI_NS_FUNCTIONS[NS_BUTTON_INIT_WITH_FRAME_CODE];
return objc_msgSend_id_rect(NSAlloc(SI_NS_CLASSES[NS_BUTTON_CODE]), func, frameRect);
}
```
Again, check the Cocoa [documentation](https://developer.apple.com/documentation/) for more information\
I would also suggest looking at Objective C example code to see if you need to use Alloc (or something else) or not
To compile and run all of the examples in a row, you can use make runExamples
to test out everything at once.
- hello-world.c - a simple application with a "Hello world" text field.
- events.c - shows the use of the NSEvent type to get the required events.
- mac-load.c - a Silicon port of hidefromkgb's original Obj-C/C example
- menu.c - shows how to create menu bars in OS X.
- button.c - shows how to create and utilize buttons using the Cocoa API.
- checkbox.c - similar to the previous example from above, however instead they're checkboxes instead of regular buttons.
- combobox.c - shows an example on how to utilize the NSComboBox class.
- trackbar.c - shows how to create & utilize a track bar and progress bar.
- save-file.c - shows how to create a SaveFileDialog.
- opengl.c - shows how to setup an OpenGL environment with an event loop.
- print.c - shows how to enable print debugging on iOS.
[todo]
[todo]
[todo]
- hidefromkgb's 'mac_load' repository - the backend for some of the important parts of Silicon (such as defining Objective-C types and functions).
- Apple - all of the headers from include/Silicon/headers have been directly copied and modified to not have any Objective-C shenanigans in them for them to be compiled in C.
- EimaMei - original creator of Silicon.
- Colleague Riley - creator of the original single-header version of Silicon (Silicon.h) and co-author of Silicon.
All of the repositories that the examples were taken from and eithered completely ported to Silicon or modified heavilly: