Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ pub const c = @cImport({
@cInclude("objc/message.h");
});

/// This is a funky helper to help with the fact that some macOS
/// SDKs have an i8 return value for bools and some have stdbool.
pub fn boolResult(comptime Fn: type, result: anytype) bool {
const fn_info = @typeInfo(Fn).@"fn";
return switch (fn_info.return_type.?) {
/// On some targets, Objective-C uses `i8` instead of `bool`.
/// This helper casts a target value type to `bool`.
pub fn boolResult(result: c.BOOL) bool {
return switch (c.BOOL) {
bool => result,
i8 => result == 1,
else => @compileError("unhandled class_addIvar return type"),
else => @compileError("unexpected boolean type"),
};
}

/// On some targets, Objective-C uses `i8` instead of `bool`.
/// This helper casts a `bool` value to the target value type.
pub fn boolParam(param: bool) c.BOOL {
return switch (c.BOOL) {
bool => param,
i8 => @intFromBool(param),
else => @compileError("unexpected boolean type"),
};
}
10 changes: 5 additions & 5 deletions src/class.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ pub const Class = struct {
}

pub fn isMetaClass(self: Class) bool {
return c.class_isMetaClass(self.value) == 1;
return boolResult(c.class_isMetaClass(self.value));
}

pub fn getInstanceSize(self: Class) usize {
return c.class_getInstanceSize(self.value);
}

pub fn respondsToSelector(self: Class, sel: objc.Sel) bool {
return c.class_respondsToSelector(self.value, sel.value) == 1;
return boolResult(c.class_respondsToSelector(self.value, sel.value));
}

pub fn conformsToProtocol(self: Class, protocol: objc.Protocol) bool {
return c.class_conformsToProtocol(self.value, &protocol.value) == 1;
return boolResult(c.class_conformsToProtocol(self.value, &protocol.value));
}

// currently only allows for overriding methods previously defined, e.g. by a superclass.
Expand Down Expand Up @@ -78,7 +78,7 @@ pub const Class = struct {
assert(fn_info.params[0].type == c.id);
assert(fn_info.params[1].type == c.SEL);
const encoding = comptime objc.comptimeEncode(Fn);
return boolResult(@TypeOf(c.class_addMethod), c.class_addMethod(
return boolResult(c.class_addMethod(
self.value,
objc.sel(name).value,
@ptrCast(&imp),
Expand All @@ -91,7 +91,7 @@ pub const Class = struct {
pub fn addIvar(self: Class, name: [:0]const u8) bool {
// The return type is i8 when we're cross compiling, unsure why.
const result = c.class_addIvar(self.value, name, @sizeOf(c.id), @alignOf(c.id), "@");
return boolResult(@TypeOf(c.class_addIvar), result);
return boolResult(result);
}
};

Expand Down
6 changes: 4 additions & 2 deletions src/object.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const std = @import("std");
const c = @import("c.zig").c;
const cpkg = @import("c.zig");
const c = cpkg.c;
const boolResult = cpkg.boolResult;
const objc = @import("main.zig");
const MsgSend = @import("msg_send.zig").MsgSend;
const Iterator = @import("iterator.zig").Iterator;
Expand Down Expand Up @@ -102,7 +104,7 @@ pub const Object = struct {
}

pub fn isClass(self: Object) bool {
return c.object_isClass(self.value) == 1;
return boolResult(c.object_isClass(self.value));
}

pub fn getInstanceVariable(self: Object, name: [:0]const u8) Object {
Expand Down
30 changes: 23 additions & 7 deletions src/protocol.zig
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
const std = @import("std");
const c = @import("c.zig").c;
const cpkg = @import("c.zig");
const c = cpkg.c;
const boolParam = cpkg.boolParam;
const boolResult = cpkg.boolResult;
const objc = @import("main.zig");

pub const Protocol = extern struct {
value: *c.Protocol,

pub fn conformsToProtocol(self: Protocol, other: Protocol) bool {
return c.protocol_conformsToProtocol(self.value, other.value) == 1;
return boolResult(c.protocol_conformsToProtocol(self.value, other.value));
}

pub fn isEqual(self: Protocol, other: Protocol) bool {
return c.protocol_isEqual(self.value, other.value) == 1;
return boolResult(c.protocol_isEqual(self.value, other.value));
}

pub fn getName(self: Protocol) [:0]const u8 {
Expand All @@ -23,13 +26,11 @@ pub const Protocol = extern struct {
is_required: bool,
is_instance: bool,
) ?objc.Property {
const isRequired: u8 = if (is_required) 1 else 0;
const isInstance: u8 = if (is_instance) 1 else 0;
return .{ .value = c.protocol_getProperty(
self.value,
name,
isRequired,
isInstance,
boolParam(is_required),
boolParam(is_instance),
) orelse return null };
}

Expand All @@ -42,3 +43,18 @@ pub const Protocol = extern struct {
pub fn getProtocol(name: [:0]const u8) ?Protocol {
return .{ .value = c.objc_getProtocol(name) orelse return null };
}

test Protocol {
const testing = std.testing;
const fs_proto = getProtocol("NSFileManagerDelegate") orelse return error.ProtocolNotFound;
try testing.expectEqualStrings("NSFileManagerDelegate", fs_proto.getName());

const obj_proto = getProtocol("NSObject") orelse return error.ProtocolNotFound;
try testing.expect(fs_proto.conformsToProtocol(obj_proto));

const url_proto = getProtocol("NSURLSessionDelegate") orelse return error.ProtocolNotFound;
try testing.expect(!fs_proto.conformsToProtocol(url_proto));

const hash_prop = obj_proto.getProperty("hash", true, true) orelse return error.ProtocolPropertyNotFound;
try testing.expectEqualStrings("hash", hash_prop.getName());
}