Reduce amount of unsafe on iOS (#2579)

* Use objc2::foundation CG types

* Add safe abstraction over UIApplication

* Add safe abstraction over UIDevice

* Add safe abstraction over UIScreen

* Add safe abstraction over UIWindow

* Add safe abstraction over UIViewController

* Add safe abstraction over UIView

* Appease clippy
This commit is contained in:
Mads Marquart 2022-12-28 18:36:32 +01:00 committed by GitHub
parent 5e77d70245
commit ee88e38f13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1048 additions and 962 deletions

View file

@ -0,0 +1,30 @@
use objc2::foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::{UIResponder, UIWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIApplication;
unsafe impl ClassType for UIApplication {
#[inherits(NSObject)]
type Super = UIResponder;
}
);
extern_methods!(
unsafe impl UIApplication {
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self, Shared>> {
unsafe { msg_send_id![Self::class(), sharedApplication] }
}
pub fn windows(&self) -> Id<NSArray<UIWindow, Shared>, Shared> {
unsafe { msg_send_id![self, windows] }
}
#[sel(statusBarFrame)]
pub fn statusBarFrame(&self) -> CGRect;
}
);

View file

@ -0,0 +1,11 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UICoordinateSpace;
unsafe impl ClassType for UICoordinateSpace {
type Super = NSObject;
}
);

View file

@ -0,0 +1,25 @@
use objc2::foundation::{MainThreadMarker, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::super::ffi::UIUserInterfaceIdiom;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIDevice;
unsafe impl ClassType for UIDevice {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl UIDevice {
pub fn current(_mtm: MainThreadMarker) -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), currentDevice] }
}
#[sel(userInterfaceIdiom)]
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
}
);

View file

@ -0,0 +1,11 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIEvent;
unsafe impl ClassType for UIEvent {
type Super = NSObject;
}
);

View file

@ -1,11 +1,30 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
mod application;
mod coordinate_space;
mod device;
mod event;
mod responder;
mod screen;
mod screen_mode;
mod touch;
mod trait_collection;
mod view;
mod view_controller;
mod window;
pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace;
pub(crate) use self::device::UIDevice;
pub(crate) use self::event::UIEvent;
pub(crate) use self::responder::UIResponder;
pub(crate) use self::view::UIView;
pub(crate) use self::view_controller::UIViewController;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode;
pub(crate) use self::touch::{UITouch, UITouchPhase, UITouchType};
pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollection};
#[allow(unused_imports)]
pub(crate) use self::view::{UIEdgeInsets, UIView};
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
pub(crate) use self::window::UIWindow;

View file

@ -0,0 +1,79 @@
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::{UICoordinateSpace, UIScreenMode};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIScreen;
unsafe impl ClassType for UIScreen {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl UIScreen {
pub fn main(_mtm: MainThreadMarker) -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), mainScreen] }
}
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self, Shared>, Shared> {
unsafe { msg_send_id![Self::class(), screens] }
}
#[sel(bounds)]
pub fn bounds(&self) -> CGRect;
#[sel(scale)]
pub fn scale(&self) -> CGFloat;
#[sel(nativeBounds)]
pub fn nativeBounds(&self) -> CGRect;
#[sel(nativeScale)]
pub fn nativeScale(&self) -> CGFloat;
#[sel(maximumFramesPerSecond)]
pub fn maximumFramesPerSecond(&self) -> NSInteger;
pub fn mirroredScreen(&self) -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), mirroredScreen] }
}
pub fn preferredMode(&self) -> Option<Id<UIScreenMode, Shared>> {
unsafe { msg_send_id![self, preferredMode] }
}
#[sel(setCurrentMode:)]
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode, Shared>, Shared> {
unsafe { msg_send_id![self, availableModes] }
}
#[sel(setOverscanCompensation:)]
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace, Shared> {
unsafe { msg_send_id![self, coordinateSpace] }
}
}
);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIScreenOverscanCompensation(NSInteger);
unsafe impl Encode for UIScreenOverscanCompensation {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
impl UIScreenOverscanCompensation {
pub const Scale: Self = Self(0);
pub const InsetBounds: Self = Self(1);
pub const None: Self = Self(2);
}

View file

@ -0,0 +1,18 @@
use objc2::foundation::{CGSize, NSObject};
use objc2::{extern_class, extern_methods, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIScreenMode;
unsafe impl ClassType for UIScreenMode {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl UIScreenMode {
#[sel(size)]
pub fn size(&self) -> CGSize;
}
);

View file

@ -0,0 +1,64 @@
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{CGFloat, CGPoint, NSInteger, NSObject};
use objc2::{extern_class, extern_methods, ClassType};
use super::UIView;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITouch;
unsafe impl ClassType for UITouch {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl UITouch {
#[sel(locationInView:)]
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
#[sel(type)]
pub fn type_(&self) -> UITouchType;
#[sel(force)]
pub fn force(&self) -> CGFloat;
#[sel(maximumPossibleForce)]
pub fn maximumPossibleForce(&self) -> CGFloat;
#[sel(altitudeAngle)]
pub fn altitudeAngle(&self) -> CGFloat;
#[sel(phase)]
pub fn phase(&self) -> UITouchPhase;
}
);
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchType {
Direct = 0,
Indirect,
Pencil,
}
unsafe impl Encode for UITouchType {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchPhase {
Began = 0,
Moved,
Stationary,
Ended,
Cancelled,
}
unsafe impl Encode for UITouchPhase {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View file

@ -0,0 +1,32 @@
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{NSInteger, NSObject};
use objc2::{extern_class, extern_methods, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITraitCollection;
unsafe impl ClassType for UITraitCollection {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl UITraitCollection {
#[sel(forceTouchCapability)]
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
}
);
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UIForceTouchCapability {
Unknown = 0,
Unavailable,
Available,
}
unsafe impl Encode for UIForceTouchCapability {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View file

@ -1,7 +1,9 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{CGFloat, CGRect, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::UIResponder;
use super::{UICoordinateSpace, UIResponder, UIViewController};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@ -12,3 +14,76 @@ extern_class!(
type Super = UIResponder;
}
);
extern_methods!(
unsafe impl UIView {
#[sel(bounds)]
pub fn bounds(&self) -> CGRect;
#[sel(setBounds:)]
pub fn setBounds(&self, value: CGRect);
#[sel(frame)]
pub fn frame(&self) -> CGRect;
#[sel(setFrame:)]
pub fn setFrame(&self, value: CGRect);
#[sel(contentScaleFactor)]
pub fn contentScaleFactor(&self) -> CGFloat;
#[sel(setContentScaleFactor:)]
pub fn setContentScaleFactor(&self, val: CGFloat);
#[sel(setMultipleTouchEnabled:)]
pub fn setMultipleTouchEnabled(&self, val: bool);
pub fn rootViewController(&self) -> Option<Id<UIViewController, Shared>> {
unsafe { msg_send_id![self, rootViewController] }
}
#[sel(setRootViewController:)]
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
#[sel(convertRect:toCoordinateSpace:)]
pub fn convertRect_toCoordinateSpace(
&self,
rect: CGRect,
coordinateSpace: &UICoordinateSpace,
) -> CGRect;
#[sel(convertRect:fromCoordinateSpace:)]
pub fn convertRect_fromCoordinateSpace(
&self,
rect: CGRect,
coordinateSpace: &UICoordinateSpace,
) -> CGRect;
#[sel(safeAreaInsets)]
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
#[sel(setNeedsDisplay)]
pub fn setNeedsDisplay(&self);
}
);
#[repr(C)]
#[derive(Debug, Clone)]
pub struct UIEdgeInsets {
pub top: CGFloat,
pub left: CGFloat,
pub bottom: CGFloat,
pub right: CGFloat,
}
unsafe impl Encode for UIEdgeInsets {
const ENCODING: Encoding = Encoding::Struct(
"UIEdgeInsets",
&[
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
],
);
}

View file

@ -1,7 +1,8 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{NSObject, NSUInteger};
use objc2::{extern_class, extern_methods, ClassType};
use super::UIResponder;
use super::{UIResponder, UIView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@ -12,3 +13,38 @@ extern_class!(
type Super = UIResponder;
}
);
extern_methods!(
unsafe impl UIViewController {
#[sel(attemptRotationToDeviceOrientation)]
pub fn attemptRotationToDeviceOrientation();
#[sel(setNeedsStatusBarAppearanceUpdate)]
pub fn setNeedsStatusBarAppearanceUpdate(&self);
#[sel(setNeedsUpdateOfHomeIndicatorAutoHidden)]
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
#[sel(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
#[sel(setView:)]
pub fn setView(&self, view: Option<&UIView>);
}
);
bitflags! {
pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1;
const PortraitUpsideDown = 1 << 2;
const LandscapeRight = 1 << 3;
const LandscapeLeft = 1 << 4;
const Landscape = Self::LandscapeLeft.bits() | Self::LandscapeRight.bits();
const AllButUpsideDown = Self::Landscape.bits() | Self::Portrait.bits();
const All = Self::AllButUpsideDown.bits() | Self::PortraitUpsideDown.bits();
}
}
unsafe impl Encode for UIInterfaceOrientationMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View file

@ -1,14 +1,32 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::UIResponder;
use super::{UIResponder, UIScreen, UIView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIWindow;
unsafe impl ClassType for UIWindow {
#[inherits(NSObject)]
type Super = UIResponder;
#[inherits(UIResponder, NSObject)]
type Super = UIView;
}
);
extern_methods!(
unsafe impl UIWindow {
pub fn screen(&self) -> Id<UIScreen, Shared> {
unsafe { msg_send_id![self, screen] }
}
#[sel(setScreen:)]
pub fn setScreen(&self, screen: &UIScreen);
#[sel(setHidden:)]
pub fn setHidden(&self, flag: bool);
#[sel(makeKeyAndVisible)]
pub fn makeKeyAndVisible(&self);
}
);