Initializing object from static variable Objective C to Swift - objective-c

Hello I am currently translating a project that was written in Objective C into swift and I am running into a puzzle. In objective C the Object (SearchItem) is a sub class of object Item and has a static variable of the same class (SearchItem). The static variable is initialized in a static function. The problem is on objective C there is a non-static function that initializes the super class variables, I tried to replicate this but I am not 100% how to approach this, I would like to keep the same format if possible, any help would be great!
Obj C:
.h file includes:
#interface SearchItem : Item
.m file includes:
static SearchItem *sharedSearchItem = nil;
+(id)sharedSearchItem {
#synchronized(self) {
if(sharedSearchItem == nil){
sharedSearchItem = [SearchItem new];
//other methods
}
}
return sharedSearchItem;
}
-(void)configureWithSettingsConfig:(SettingsConfig *)settings{
NSLog(#"%#", [super initWithSettings:settings]);
//Other methods
}
Swift:
static var sharedSearchItem: SearchItem? = nil
static func sharedSearchItemInit(){
if(sharedSearchItem == nil){
sharedSearchItem = SearchItem()
//Other methods
}
}
func configureWithSettingsConfig(settings: SettingsConfig){
print(SearchItem.init(settings: settings)) // creates separate object need it to be on same instantiation
/*
The following calls won’t work
self = ServiceFacade.init(settings: settings)
self.init(settings: settings)
super.init(settings: settings)*/
//Other methods
}

In Swift the way we create Singletons is simply like so:
static let sharedSearchItem = SearchItem()
That's it. No need for a special "sharedInit" function.

Related

self class alloc for controllers equivalent in Swift

I had a class UIBaseClassViewController with convenient functions in objective c.Now i'm switching to swift and i'm trying to convert it's code into swift.the function giving me problem is
+(UIBaseClassViewController*)getController
{
return [[[self class] alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
}
i was able to convert it but it's not working fine
static func getController() -> Self
{
print("sam controller class = \(String(describing:self))")
print("SAM controller = \(self.init(nibName: String(describing:self), bundle:Bundle.main))")
return self.init(nibName: String(describing:self), bundle:Bundle.main)
}
Output:
sam controller class = UILoginViewController
SAM controller = <Swift_And_Node.UIBaseClassViewController: 0x7f8a4ee13830>
created object is of type UIBaseClassViewController.it loads the nib fine but as object is of UIBaseClassViewController app crashes because it was not able to find functions in UIBaseClassViewController which are in UILoginViewController.
How can i make it create object of child class instead of parent.UILoginViewController in this case
for better Understanding showing adding code:
UIBaseClassViewController:
class UIBaseClassViewController: UIViewController {
static func getController() -> Self
{
print("sam controller class = \(String(describing:self))")
print("SAM controller = \(self.init(nibName: String(describing:self), bundle:Bundle.main))")
var object = self
return self.init(nibName: String(describing:self), bundle:Bundle.main)
}
}
UILoginViewController:
class UILoginViewController: UIBaseClassViewController {}
3rd controller who need UILoginViewController:
UILoginViewController.getController()
You either have to call this static function on desired view controller class or not making it static at all. Please see the example below to see how it works in Swift.
class ParentView: UIView {
static func printSelf() {
print(String(describing: self))
}
}
class ChildView: ParentView {}
ParentView.printSelf() // Prints ParentView
ChildView.printSelf() // Prints ChildView
Turns out we don't need to mention nib and bundle for controller object...I moved from objective c and these things are necessary there.
with
[[UILoginViewController alloc] init]
app will show black screen.
In swift we can just use UILoginViewController() and it will automatically associate nib with the controller object.
so to answer my question i just used
self.init()
instead of
self.init(nibName: String(describing:self), bundle:Bundle.main)

Calling original function from swizzled function

I am messing around with method swizzling and would like to call the original function after performing a method_exchangeImplementations. I have two projects I have setup for this.
The first project is the main project for the application. This project includes all of the logic for the application. Notice that originalMethodName is called when the view loads.
#implementation ViewController
- (void)originalMethodName
{
NSLog(#"REAL %s", __func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"REAL %s", __func__);
[self originalMethodName];
}
#end
The second project includes only the code for swizzling. I have a method swizzle_originalMethodName which includes the code I want to inject into the main application with the originalMethodName function is called.
#implementation swizzle_ViewController
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
}
__attribute__((constructor)) static void initializer(void)
{
NSLog(#"FAKE %s", __func__);
Class c1 = objc_getClass("ViewController");
Class c2 = [swizzle_ViewController class];
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
Method m2 = class_getInstanceMethod(c2, #selector(swizzle_originalMethodName));
method_exchangeImplementations(m1, m2);
}
#end
The swizzle is working just fine (as seen in the output below), but now I want to be able to call originalMethodName from the swizzle_originalMethodName
2016-08-17 14:18:51.765 testMacOS[7295:1297055] FAKE initializer
2016-08-17 14:18:51.822 testMacOS[7295:1297055] REAL -[ViewController viewDidLoad]
2016-08-17 14:18:51.822 testMacOS[7295:1297055] FAKE -[swizzle_ViewController swizzle_originalMethodName]
I have tried to use NSInvocation but am not having any luck. Any ideas what I am doing wrong?
Class c1 = objc_getClass("ViewController");
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding( m1)];
NSInvocation *originalInvocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[originalInvocation invoke];
If you are swizzling within a class hierarchy, e.g. you have a subclass which swizzles one of its ancestors methods with one of its own, then you simply have the swizzled-in method apparently call itself – that call will actually call the swizzled-out method as the methods have been swapped. In your case you would have:
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
[self swizzle_originalMethodName]; // call original
}
This does not work in your case as you are cross-class swizzling, so self doesn't reference the class with the swizzled-out method. And you don't have an instance of the swizzling class you can call the swizzled-out method on...
Here is one easy way to fix this, what your swizzle-in method needs to be able to do is call the original implementation, and you can get that when you setup the swizzling.
In Objective-C a method is implemented by a function whose first two arguments are the object reference the method is being called on and the selector and the remaining arguments are those of the method. For example the NSString method:
- (NSRange)rangeOfString:(NSString *)aString
is implemented by a function something like:
NSRange rangeOfStringImp(NSString *self, SEL cmd, NSString *aString)
You can obtain a function pointer to this implementation function using method_getImplementation.
To your code, first in your swizzle_ViewController declare a type for the implementation function of the method you are swizzling, and a global to store the function pointer:
typedef void (*OriginalImpType)(id self, SEL selector);
static OriginalImpType originalImp;
Now in your initializer method you need to save the method implementation, you can do this by adding the line shown:
Method m1 = class_getInstanceMethod(c1, #selector(originalMethodName));
originalImp = (OriginalImpType)method_getImplementation(m1); // save the IMP of originalMethodName
Finally have your swizzled-in method call the saved implementation:
- (void)swizzle_originalMethodName
{
NSLog(#"FAKE %s", __func__);
originalImp(self, #selector(originalMethodName)); // call the original IMP with the correct self & selector
}
Optional: The above works correctly, however it does a little more than is required – the method implementations are both exchanged and one is stored in a global variable, all you really need to do is save the original implementation of m1 and then set its implementation to that of m2. You can address this by replacing the call to method_exchangeImplementations with:
method_setImplementation(m1, method_getImplementation(m2));
It is a little more typing, but somewhat clearer as to what actually needs to be done.
HTH
There is a slightly easier option to call the original implementation that doesn't require you to store the method implementation directly. When you exchange implementations of the methods, the original implementation will be stored in the swizzler class. You can fetch the swizzled out implementation using the class_getMethodImplementation function. Here is a playground sample:
import Cocoa
let fooSelector = Selector("fooWithArg:")
let swizzledFooSelector = Selector("swizzled_fooWithArg:")
class A: NSObject {
#objc dynamic func foo(arg: String) {
print("Foo \(arg) in A")
}
}
class B: NSObject {
private typealias FooFunc = #convention(c) (AnyObject, Selector, String) -> Void
#objc func swizzled_foo(arg: String) {
print("Swizzled_foo \(arg) in B")
unsafeBitCast(
class_getMethodImplementation(B.self, swizzledFooSelector),
to: FooFunc.self
)(self, fooSelector, arg)
}
}
method_exchangeImplementations(
class_getInstanceMethod(A.self, fooSelector)!,
class_getInstanceMethod(B.self, swizzledFooSelector)!
)
A().foo(arg: "bar")

swift init not visible in objective-C

I'm trying to create init functions in Swift and create instances from Objective-C. The problem is that I don't see it in Project-Swift.h file and I'm not able to find the function while initializing. I have a function defined as below:
public init(userId: Int!) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId
}
I even tried putting #objc(initWithUserId:) and I keep getting the same error again. Is there anything else I'm missing? How do I get the constructor visible to Objective-C code?
I read the below for this:
https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/Initialization.html
https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/interactingwithobjective-capis.html
How to write Init method in Swift
How to define optional methods in Swift protocol?
The issue you're seeing is that Swift can't bridge optional value types -- Int is a value type, so Int! can't be bridged. Optional reference types (i.e., any class) bridge correctly, since they can always be nil in Objective-C. Your two options are to make the parameter non-optional, in which case it would be bridged to ObjC as an int or NSInteger:
// Swift
public init(userId: Int) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId
}
// ObjC
MyClass *instance = [[MyClass alloc] initWithUserId: 10];
Or use an optional NSNumber?, since that can be bridged as an optional value:
// Swift
public init(userId: NSNumber?) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId?.integerValue
}
// ObjC
MyClass *instance = [[MyClass alloc] initWithUserId: #10]; // note the #-literal
Note, however, you're not actually treating the parameter like an optional - unless self.userId is also an optional you're setting yourself up for potential runtime crashes this way.
use this one:
var index: NSInteger!
#objc convenience init(index: NSInteger) {
self.init()
self.index = index
}

Generate an Obj-C Method

Is it possible to generate an Obj-C method? - For example:
- (void)doSomething:(BOOL)isLater
{
if (isLater == FALSE)
{
NSLog(#"Not Later");
} else {
NSLog(#"Is Later");
}
}
I'm specifically talking about generating the logic, I have some code that can create a class and add an existing method, but I'd like to know If I can generate the logic for the method itself and add it to a method?
static NSString *Description(id self, SEL _cmd)
{
return #"My Description Method";
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Class mySubclass = objc_allocateClassPair([NSObject class], "MySubclass", 0);
// grab NSObject's description signature so we can borrow it
Method description = class_getInstanceMethod([NSObject class],
#selector(description));
const char *types = method_getTypeEncoding(description);
// now add
class_addMethod(mySubclass, #selector(description), (IMP)Description, types);
objc_registerClassPair(mySubclass);
id myInstance = [[mySubclass alloc] init];
NSLog(#"%#", myInstance);
}
If we are splitting hair:
Yes, technically, you can.
But it won't be easy at all.
Objective-C can be parsed by libclang (the C++ library which the clang compiler is based on) and then JIT-compiled and run using LLVM.
This way, you can embed clang and LLVM into your program, generate some Objective-C source text dynamically, parse it into an abstract syntax tree (or generate that AST directly, without writing and parsing any Objective-C), and compiling it. If you write a method that does this, then you can essentially run arbitrary Objective-C code dynamically from within your application.

passing in a method in Objective C

In C# you can create a delegate method, assign it to a variable or pass it into a method as if it were a variable. For example:
public delegate int Lookup(String s);
//...
public static int Evaluate(String exp, Lookup variableEvaluator)
{
//...
}
I heard that in C you can create a pointer to any method and then pass that pointer to a method.
Can anyone give me a simple example of doing that in Objective-C? Of course, I can create an object with a singe method and pass that object into a method. But I am curious if there is a way of doing that similar to that of C# or C.
Lots of ways.
One: the good. Use blocks (closures, lambda calculus, however you call it):
typedef void (^MyCallback)();
- (void)callTheCallback:(MyCallback)blockToInvoke
{
blockToInvoke();
}
MyCallback cb = ^{
NSLog(#"I was called! :D");
};
[self callTheCallback:cb];
Two: the bad. Grab a pointer to the method function itself and call that. (Warning: if you use this approach, I'll sue you.)
- (void)callTheCallback:(IMP)funcPtrToCall withObject:(id)obj selector:(SEL)sel
{
funcPtrToCall(obj, sel);
}
- (void)someCallbackMethod
{
NSLog(#"I was called! :D");
}
IMP implemt = [[self class] instanceMethodForSelector:#selector(someCallbackMethod)];
[self callTheCallback:implemt withObject:self selector:#selector(someCallbackMethod)];
Three: the ugly. Use a delegate:
- (void)delegateMethodOfSomeObject:(SomeObject *)obj
{
NSLog(#"I was called! :D");
}
SomeObject *obj = [[SomeObject alloc] init];
obj.delegate = self;
[obj makeThisObjectSomehowCallItsDelegateThatIsCurrentlySelf];
Two quick thoughts come to mind.
The short answer is called "blocks", but it's lower level than is probably recommended for what you need.
The "cleaner" solution (read: higher level) is to pass two params: and object (called "target") and a selector (called "action"). This is a very common pattern in Objective-C, so I'll only demonstrate this one. If you are interested in the blocks idea, check out this doc.
Essentially, the object should be passed as an id, and the selector as a SEL, for which we have the handy #selector() construct:
-(void) doThingWithTarget:(id) targetObj action:(SEL) actionSel {
if([targetObj respondsToSelector:actionSel]) {
[targetObj performSelector:actionSel withObject:self];
}
}
// ...
[thatOtherObject doThingWithTarget:self action:#selector(myMethod:)];
// ... where
-(void) myMethod:(id) sender {
// sender is the calling object, or should be by contract.
}
Objective C uses selectors. http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocSelectors.html