💡 A light Swift wrapper around Objective-C Runtime

Overview

LMMethod Methods

A light wrapper around Objective-C Runtime.

What exactly is lumos?

lumos as mentioned is a light wrapper around objective-c runtime functions to allow an easier access to the runtime. It makes operations such as swizzling and hooking very simple in Swift.

For example, say you wish to run a block of code whenever a ViewController's viewDidLoad method is called

With lumos, you can do the following:

// In AppDelegate (or any conveinient place)..

let method = Lumos.for(ViewController.self).getInstanceMethod(selector: #selector(ViewController.viewDidLoad))
        
method?.prepend {
    // This block will be run every time a viewDidLoad is called
    print("View Controller loaded")
}

Similarily you can append a block to a method which will be called right before the method returns. You can even use replace to replace the method's implementation with the block you pass in as a parameter.

If you wanted more flexibility, you could swizzle the viewDidLoad method using the following lines:

@objc func myMethod() {
    // Do anything here
}

let myMethod = self.lumos.getInstanceMethod(selector: #selector(myMethod))

method?.swapImplementation(with: myMethod)

Do you feel the superpower yet? Maybe you wish to list all the classes registered at runtime:

Lumos.getAllClasses()

Fun Fact: There are almost 12,000 classes registered at runtime Try Lumos.getAllClasses().count

You could get the class hierarchy of any class just with:

myObject.lumos.getClassHierarcy()   // For UIView: [UIView, UIResponder, NSObject]

Fun Fact: Some classes such as URLSessionTask are actually dummy classes which are replaced with underlying classes such as __NSCFLocalSessionTask during runtime.

With lumos, you can iterate through variables, functions, protocols etc and meddle with them at runtime. Have fun exploring!

Usage

Just incantate .lumos on any instance of a NSObject subclass or use Lumos.for(object) for where object is of type AnyClass, AnyObject, Protocol, Ivar, objc_property_t or objc_property_attribute_t.

LMMethod Methods

LMClass Methods

P.s The code itself is the documentation for now. There are many more methods that lumos offers which are not discussed in this document. Cheers :)

Why lumos?

The Objective-C Runtime provides many powerful methods to manipulate objects, classes and methods at runtime. Although disasterous when misused, these methods provide a great way to peek into the runtime and meddle with it.

However, the methods are not exactly easy to use sometimes. For example the following method is used to obtain a list of all classes registered at runtime:

func objc_getClassList(_ buffer: AutoreleasingUnsafeMutablePointer<AnyClass>?, _ bufferCount: Int32) -> Int32

Often, a lot of dirty work needs to be done before one gets the list out. Here is how I would do it:

static func getClassList() -> [AnyClass] {
    let expectedClassCount = objc_getClassList(nil, 0)
    let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))

    let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass>(allClasses)
    let actualClassCount: Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)

    var classes = [AnyClass]()
    for i in 0 ..< actualClassCount {
        if let currentClass: AnyClass = allClasses[Int(i)] {
            classes.append(currentClass)
        }
    }

    allClasses.deallocate()
    return classes
}

Now all you would need to do to obtain the list of classes would be to invoke this method. Maybe you wish to get a list of classes that conform to a certain protocol:

static func classesImplementingProtocol(_ requiredProtocol: Protocol) -> [AnyClass] {
    return Lumos.getClassList().filter { class_conformsToProtocol($0, requiredProtocol) }
}

Perhaps you wish to swizzle method implementations at runtime:

static func swizzle(originalClass: AnyClass, originalSelector: Selector, swizzledClass: AnyClass, swizzledSelector: Selector) {
    guard let originalMethod = class_getInstanceMethod(originalClass, originalSelector),
    let swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector) else {
        return
    }

    let didAddMethod = class_addMethod(originalClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

    if didAddMethod {
        class_replaceMethod(originalClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

You can now use:

Lumos.swizzle(originalClass: URLSessionTask,
              originalSelector: #selector(URLSessionTask.resume),
              swizzledClass: SwizzledSessionTask,
              swizzledSelector: #selector(SwizzledSessionTask.resume))

P.S you might want to use dispatch_once with the method above to above swizzling more than once across multiple threads.

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate lumos into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'Lumos'
end

Then, run the following command:

$ pod install

License

Lumos is released under the Apache-2.0. See LICENSE for details.

You might also like...
Catch Objective-C exceptions in Swift

ExceptionCatcher Catch Objective-C exceptions in Swift There are many Cocoa APIs that can throw exceptions that cannot be caught in Swift (NSKeyedUnar

Swift Property Wrappers, but in Objective-C. And done horribly.

TOPropertyAccessor is an open source, Objective-C abstract class. Similar to Realm's Cocoa API, it uses the dynamic nature of the Objective-C runtime to access the properties of any of its subclasses, and routes calling them through overridable access points.

A reverse engineering tool to restore stripped symbol table and dump Objective-C class or Swift types for machO file.

A reverse engineering tool to restore stripped symbol table and dump Objective-C class or Swift types for machO file.

Reflection for enumerations in Objective-C.

ReflectableEnum A macro and a set of functions introducing reflection for enumerations in Objective-C. Features: get a string value for an enumeration

Because Objective-C should have inherited more from Smalltalk

OpinionatedC Sometimes, Objective-C is just overly verbose. Life is too short to enumerateObjectsUsingBlock and who has the time to create sub-arrays

A Cocoa library to extend the Objective-C programming language.

The Extended Objective-C library extends the dynamism of the Objective-C programming language to support additional patterns present in other programm

The Objective-C block utilities you always wish you had.

BlocksKit Blocks in C and Objective-C are downright magical. They make coding easier and potentially quicker, not to mention faster on the front end w

Proper YAML support for Objective-C. Based on recommended libyaml.

YAML.framework for Objective-C Based on C LibYAML library (http://pyyaml.org/wiki/LibYAML) by Kirill Simonov. YAML.framework provides support for YAML

A quick and "lean" way to swizzle methods for your Objective-C development needs.

Swizzlean A quick and "lean" way to swizzle methods for your Objective-C development needs. Adding Swizzlean to your project Cocoapods CocoaPods is th

Comments
  • Crash upon accessing anything from Lumos.getAllClasses()

    Crash upon accessing anything from Lumos.getAllClasses()

    Doing something like this:

    let classes =  Lumos.getAllClasses().filter { b in
          return true
    }
            
    print(classes[0].description())
    

    crashes with:

    [ description] sent to deallocated instance 0x123195e58

    The filtered array wasn't empty, sure thing. Xcode 9.4.1.

    opened by reenboog 4
  • Dummy classes swizzling

    Dummy classes swizzling

    Fun Fact: Some classes such as URLSessionTask are actually dummy classes which are replaced with underlying classes such as __NSCFLocalSessionTask during runtime.

    Just wondering is it possible to swizzle __NSCFLocalSessionTask using Lumos?

    opened by shams-ahmed 0
  • Swift API Guidelines

    Swift API Guidelines

    Small nitpick here, the Swift API Guidelines state that getters should not begin with get, as it's implicit in the fact that the method returns something.

    Maybe change things like:

    func getMethods() -> [Selector] { ... }
    func getImplementation(selector: Selector) -> IMP? 
    

    to

    var methods: [Selector] { ... }
    func implementation(for selector: Selector) -> IMP?
    
    opened by harlanhaskins 1
Owner
Suyash Shekhar
learning how to learn
Suyash Shekhar
Swift-friendly API for a set of powerful Objective C runtime functions.

ObjectiveKit ObjectiveKit provides a Swift friendly API for a set of powerful Objective C runtime functions. Usage To use ObjectiveKit: Import Objecti

Roy Marmelstein 850 Oct 25, 2022
Swift-ndi - Swift wrapper around NewTek's NDI SDK

swift-ndi Swift wrapper around NewTek's NDI SDK. Make sure you extracted latest

Alessio Nossa 12 Dec 29, 2022
An unofficial wrapper around FSEvent tailored for Swift 5.

EonilFSEvents Eonil 2018 Maintenance. 2019 Maintenance. It's possible to use FSEvents directly in Swift, but it still involves many boilerplate works

eonil 87 Dec 24, 2022
Swift Server Implementation - RESTful APIs, AWS Lambda Serverless For Swift Runtime amazonlinux: AWS Lambda + API Gateway

Swift Server Implementation - RESTful APIs, AWS Lambda Serverless For Swift Runtime amazonlinux: AWS Lambda + API Gateway deployed on Graviton arm64 build swift:5.6.2-amazonlinux2-docker image

Furqan 2 Aug 16, 2022
Plugin and runtime library for using protobuf with Swift

Swift Protobuf Welcome to Swift Protobuf! Apple's Swift programming language is a perfect complement to Google's Protocol Buffer ("protobuf") serializ

Apple 4.1k Dec 28, 2022
A simple utility allowing to detect Swift version at runtime.

SwiftVersionDetector SwiftVersionDetector allows you to detect Swift version at runtime. Note that detecting the Swift version of the machine on which

Alessandro Venturini 2 Dec 3, 2022
Contacts wrapper for iOS 9 or upper with Objective-C

ContactsWrapper Contacts wrapper for iOS 9 or upper with Objective-C. For the information translated to Russian, take a look at this link. Requirement

Abdullah Selek 22 Jun 18, 2022
The missing light persistence layer for Swift

If you're planning on using Swift 4 in the near future, please consider using the new Codable protocols which provide the same functionality as Pantry

Nick O'Neill 842 Sep 11, 2022
Automatically set your keyboard's backlight based on your Mac's ambient light sensor.

QMK Ambient Backlight Automatically set your keyboard's backlight based on your Mac's ambient light sensor. Compatibility macOS Big Sur or later, a Ma

Karl Shea 29 Aug 6, 2022
Soulful docs for Swift & Objective-C

jazzy is a command-line utility that generates documentation for Swift or Objective-C About Both Swift and Objective-C projects are supported. Instead

Realm 7.2k Jan 1, 2023