This project is a Swift interface for GNUStep's version of AppKit.
This project assumes that you are running OnFlapp's GNUStep Desktop project. To build, you need to install GNUStep Desktop [https://onflapp.github.io/gs-desktop/index.html] on Debian, then install Swift version 5.8.1 on Debian.
IMPORTANT: This assumes that file system layout of Onflapp's GNUstep Desktop environment.
I haven't yet figured out how to configure Swift to copy the files into the a folder called Resources
within the same folder holds the built Swift executable. For now, just copy in the Resources folder into the .build/build/debug folder.
AppKit is a Swif tlibrary that wraps GNUStep's gui library. HelloWorld is my playground. It is often not working. NSWindowTest is to strictly test the code that creates NSWindow. It runs well!
This is a class written in Swfit that is responsible to instantiate GNUStep ObjC classes. When it instantiates a new NSObject, it always calls retain. If it is just wrapping a alreday made pointer, currently it does not call retain.
If you intend to wrap a new subclass of an existing NSObject, Objective-C object, then you need to implement GNUStepNSObjectSubclassConstructor
first, then you can wrap that subclass.
This class is responsible for adding new objects to the GNUStep ObjC runtime. Every new object that it creates gets an additional Ivar called ___swiftPtr
that hols a pointer to the Swift object that it wraps.
Currently, I have a class called GNUStepNSObjectWrapper
that holds a reference to a NSObject in the GNUStep ObjC runtime. This object retains any NSObjects that is creates. However, we may get that same object back in the form of an id
from the GNUStep ObjC2 runtime via a call to objc_msgSend
or smart_swift_lookupIvar
.
When we recieve that object we either have to (a) construct a new wrapper calss. This is easy, we can just call object_getClassName()
and use the retrieved name to map it to a Swift class that will wrap that id
or (b) we can take the id
value and look up a Swift object that was already instantiated and wraps it.
Question 1: How do we manage retains?
Question 2 What is the best way to store a table of the objects?
One of the major issues that I faced was getting objc_msgSend
and objc_msgSend_stret
to play nicely with swift. Each of these functions are used to send messages (call methods) of an Objective-C object. Unfortunatley, I never got that working right, instead, I implemented a funciton objc_smart_getIMP
which calls class_getMethodImplementation
. The term 'smart' here just means that I am making the function swiftier, and it helps do lookup and casting for me. There are two version of this function
public func objc_smart_getIMP<T>(object: GNUStepNSObjectWrapper, selector: String) -> T? {
let c = object_getClass(&object._nsobjptr!.pointee)
let v = class_getMethodImplementation(c, sel_getUid(selector))
let rt: T? = unsafeBitCast(v, to: T.self)
return rt
}
public func objc_smart_getIMP<T>(id: GNUStepNSObjectPointer, selector: String) -> T? {
var id = id
let c = object_getClass(id)
let v = class_getMethodImplementation(c, sel_getUid(selector))
let rt: T? = unsafeBitCast(v, to: T.self)
return rt
}
Classes like NSArray and NSDictionary suffer from only being able to store NSObjects. It may be nice to be able to put Swift objects into these arrays. We would need to wrap them into something in order to do this.
An NSSwiftObject wrapper type could be introduced which can hold a pointer back to the original Swift object; however, would need to manage retaining any Swift reference types. Some ideas are presented here. In this blog that may help us construct this. It would be nice if this was a part of the GNUStep's Foundation.
https://theswiftdev.com/how-to-use-a-swift-library-in-c/
objc_msgSend() is a c function used by the Objective-C runtime to send messages. Unfortunately it has a variable arguments which cannot be imported into Swift. To overcome this, I have created some specialized versions of of objc_msgSend that have different number of arguments. These can be found in the ObjCSwiftInterop.c file.
Ultimately, I would like a swift version of this which more intelligently decides how to map the values. I started work on this, in the AppKit.swift file called func objc_smart_sendMessage<T>(object: NSObjectGNUStepSwiftBridge, selector: String, value1: Any?, value2: Any?, value3: Any?, value4: Any?, value5: Any?, value6: Any?, value7: Any?, value8: Any?, value9: Any?) -> T?
This solves the problem that we have to send messages of arbitrary side, but it does not solve the issues around casting, and that two separate functions objc_msgSend
and objc_msgSend_stret
depending on which type we are going to get back from.
Just some thoughts and notes: Please correct me where I am wrong or misunderstanding.
objc_msgSend
and objc_msgSend_stret
are very difficult functions to properly import into Swift. objc_msgSend
is used for messages that return Objective-C classes, and simple data values that have the same size as id
. From what I understand, both require the the function to be properly cast to work and because they are implemented in assembly and they are doing some magic with the registers to properly get the result. objc_msgSend
returns void*
but objc_msgSend_stret
on GNUStep's runtime returns void
. Before you can use objc_msgSend_stret
, you have to cast it to a function that returns the value you are expecting.
UIView *view = [[NSView alloc] initWithFrame:NSRectZero];
NSRect (*sendRectFn)(id receiver, SEL operation);
sendRectFn = (NSRect(*)(id, SEL))objc_msgSend_stret;
NSRect frame = sendRectFn(view, @selector(frame));
Idea 1: THIS DOES NOT WORK. I really have no idea what I am doing. LOL
void* forSwift_objcMsgSend_stret(id ID, SEL cmd, int64_t returnSize) {
printf("forSwift_objcMsgSend_stret\n");
void* itemArr = malloc(returnSize);
void* (*sendRectFn)(id receiver, SEL operation);
sendRectFn = (void(*)(id, SEL))objc_msgSend_stret;
itemArr = sendRectFn(ID, cmd);
return itemArr;
}
**Inspiration: 1 **
This stackoverflow post suggests using class_getMethodImplementation_stret
.
https://stackoverflow.com/questions/54002375/swift-runtime-calling-superclass-method
What we need to be able to do is express this in Swift using
I do not know how the objc_msgSend_stret
knows what the returned data type should be.