Skip to content

Instantly share code, notes, and snippets.

@cprovatas
Last active June 1, 2023 01:34
Show Gist options
  • Save cprovatas/98ff940140c8744c4d1f3bcce7ba4543 to your computer and use it in GitHub Desktop.
Save cprovatas/98ff940140c8744c4d1f3bcce7ba4543 to your computer and use it in GitHub Desktop.
Block-Based Selectors in Swift
//
// BlockBasedSelector.h
//
// Created by Charlton Provatas on 11/2/17.
// Copyright © 2017 CharltonProvatas. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface BlockBasedSelector : NSObject
@end
typedef void (^OBJCBlock)(id _Selector);
typedef void (^OBJCBlockWithSender)(id _Selector, id sender);
void class_addMethodWithBlock(Class class, SEL newSelector, OBJCBlock block);
void class_addMethodWithBlockAndSender(Class class, SEL newSelector, OBJCBlockWithSender block);
//
// BlockBasedSelector.m
//
// Created by Charlton Provatas on 11/2/17.
// Copyright © 2017 CharltonProvatas. All rights reserved.
//
#import "BlockBasedSelector.h"
#import <objc/runtime.h>
@implementation BlockBasedSelector
@end
void class_addMethodWithBlock(Class class, SEL newSelector, OBJCBlock block)
{
IMP newImplementation = imp_implementationWithBlock(block);
Method method = class_getInstanceMethod(class, newSelector);
class_addMethod(class, newSelector, newImplementation, method_getTypeEncoding(method));
}
void class_addMethodWithBlockAndSender(Class class, SEL newSelector, OBJCBlockWithSender block)
{
IMP newImplementation = imp_implementationWithBlock(block);
Method method = class_getInstanceMethod(class, newSelector);
class_addMethod(class, newSelector, newImplementation, method_getTypeEncoding(method));
}
//
// BlockBasedSelector.swift
// Parking
//
// Created by Charlton Provatas on 11/9/17.
// Copyright © 2017 Charlton Provatas. All rights reserved.
//
import Foundation
// swiftlint:disable identifier_name
func Selector(_ block: @escaping () -> Void) -> Selector {
let selector = NSSelectorFromString("\(CFAbsoluteTimeGetCurrent())")
class_addMethodWithBlock(_Selector.self, selector) { _ in block() }
return selector
}
/// used w/ callback if you need to get sender argument
func Selector(_ block: @escaping (Any?) -> Void) -> Selector {
let selector = NSSelectorFromString("\(CFAbsoluteTimeGetCurrent())")
class_addMethodWithBlockAndSender(_Selector.self, selector) { (_, sender) in block(sender) }
return selector
}
// swiftlint:disable identifier_name
let Selector = _Selector.shared
@objc class _Selector: NSObject {
static let shared = _Selector()
}
@cprovatas
Copy link
Author

screen shot 2017-11-02 at 4 26 30 pm
screen shot 2017-11-02 at 4 12 24 pm

@0xLeif
Copy link

0xLeif commented Feb 27, 2019

@cprovatas I converted your obj-c code into swift, but sadly I am getting nil from class_getInstanceMethod

func class_addMethodWithBlock(class c: AnyClass, newSelector: Selector, block: @escaping @convention(block) () -> Void) {

    let imp = imp_implementationWithBlock(block)
    guard let method = class_getInstanceMethod(c, newSelector) else {
        fatalError("class_getInstanceMethod")
    }
    class_addMethod(c, newSelector, imp, method_getTypeEncoding(method))
}

func class_addMethodWithBlockAndSender(class c: AnyClass, newSelector: Selector, block: @escaping @convention(block) (Any?) -> Void) {
    let imp = imp_implementationWithBlock(block)
    guard let method = class_getInstanceMethod(c, newSelector) else {
        fatalError("class_getInstanceMethod")
    }
    class_addMethod(c, newSelector, imp, method_getTypeEncoding(method))
}

@iosparesh
Copy link

It leaks memory in swift 5, can you please help me

@emoonadev
Copy link

emoonadev commented Jan 6, 2020

@cprovatas I converted your obj-c code into swift, but sadly I am getting nil from class_getInstanceMethod

func class_addMethodWithBlock(class c: AnyClass, newSelector: Selector, block: @escaping @convention(block) () -> Void) {

    let imp = imp_implementationWithBlock(block)
    guard let method = class_getInstanceMethod(c, newSelector) else {
        fatalError("class_getInstanceMethod")
    }
    class_addMethod(c, newSelector, imp, method_getTypeEncoding(method))
}

func class_addMethodWithBlockAndSender(class c: AnyClass, newSelector: Selector, block: @escaping @convention(block) (Any?) -> Void) {
    let imp = imp_implementationWithBlock(block)
    guard let method = class_getInstanceMethod(c, newSelector) else {
        fatalError("class_getInstanceMethod")
    }
    class_addMethod(c, newSelector, imp, method_getTypeEncoding(method))
}

Me too return me nil, There is an updated version of the code in swift?

@cprovatas
Copy link
Author

cprovatas commented Jan 6, 2020

You have to import that class as an objective-c class as there are some issues with how the blocks are bridged into Swift. Just import 'BlockBasedSelector.h' into your bridging header file and everything should work as expected.

@lukya
Copy link

lukya commented Apr 2, 2020

There is no license information. Can this be made available for consumption in a commercial application with a good open license? Or is ther any other way to license this for such use?

@cprovatas
Copy link
Author

cprovatas commented Apr 2, 2020

@lukya yes feel free to use it. this snippet was written independently by myself and isn't the IP of any other entity

@qosh-dev
Copy link

qosh-dev commented Dec 29, 2020

Hi maybe you can help me. I take an error when I run method Selector : unrecognized selector sent to instance 0x10dd44ce0'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment