0

I am trying to convert some swift function to objective-c

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

It is a code from Load a UIView from nib in Swift

I thought .h file can be wrote like this

@interface UIView <T: UIView *> (MyCommon)
+ (T) loadFromNib;
@end

And I got error like this

Category of non-parameterized class 'UIView' cannot have type parameters

Is that means it's impossible to implement UIView extension function using Generic in objective C?

naljin
  • 439
  • 3
  • 10
  • I *think* you are looking for a category, take a look at this [answer](https://stackoverflow.com/questions/863321/how-to-load-a-uiview-using-a-nib-file-created-with-interface-builder/3191405#3191405/) – skaak Aug 15 '20 at 13:55
  • @skaak Thank you for your recommendation. I helped a lot. Add to that, I want to restrict generic type like `` . But I can only find Generic `T` . Can you give me more advice? – naljin Aug 15 '20 at 15:03
  • Ok, in that answer the code starts like this ```+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {``` just replace it as follows ```+ (UIView*)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {``` ... I think generics here is overkill. The ```id``` means *anything* and the change to ```UIView``` means it must be a UIView or subclass. – skaak Aug 15 '20 at 15:17

1 Answers1

0

That answer I mentioned use a NSObject category. Maybe change that to a UIView category as below.

EDIT

Since you want a MyView see the changes below and try that. Note that in Objective-C you get a warning while Swift will slap you on the wrist for such code ...

@class MyView;

@interface UIView (loadFromNib)

@end

@implementation UIView (LoadFromNib)

// This only returns UIView or subclass ... or nil
+ ( UIView * )loadFromNib:( NSString * ) name
{
    // For the adventurous
    return [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil].firstObject;

    // For the cautious
    for ( NSObject * i in [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] )
    {
        if ( [i isKindOfClass:UIView.class] )
        {
            return i;
        }
    }

    // For the unsuccessful
    return nil;
}

// This returns MyView
+ ( MyView * ) loadMyViewFromNib:( NSString * ) name
{
    return [UIView loadFromNib:name];
}

@end
skaak
  • 2,988
  • 1
  • 8
  • 16
  • Ok great - you will see I gave two ways to return the UIView in the code. The first works in almost all cases and is used widely I believe, but sometimes for certain NIBs it will fail, and the second one is safer. – skaak Aug 15 '20 at 15:50
  • I change it to `UIView` category and make it return UIView. And it was successful to create UIView like this. `let myView: MyView = UIView.loadFromNib(from: MyView.self) as! MyView` But I am not satisfied with that I always need to type casting like `as! MyView` Can’t Objective-C infer my type by referencing parameter that I passed so that I don’t need to do type cast? – naljin Aug 15 '20 at 15:53
  • I think our comments crossed! In Objective-C you do not need to typecast, it is on the Swift side that you are bound by much more strictness. I am not very fluent in Swift and can not help you with that - in fact that is one of the reasons I am not that fluent in Swift. I think that exclamation is because the function may return nil and you have to help Swift in attaching meaning to it. – skaak Aug 15 '20 at 15:58
  • I really have little Swift skills but I think you can get away with this ```let myView: MyView = UIView.loadFromNib(from: MyView.self)!``` - alternatively you can declare the function to return NONNULL but that is not what you want here. – skaak Aug 15 '20 at 16:08
  • Why I need to add exclamation is when I call that function, it only knows the return type is `UIView`, so I need to assert it is the type `MyView`. If Objective-C doesn't need to type cast, how can I return specific type depending on the type that I pass? – naljin Aug 15 '20 at 16:09
  • I think I understand the problem you are trying to solve here. But it is simply not a problem in Objective-C. In Objective-C you are free to e.g. do ```AnyView * av = x``` even if ```x``` returns a UIView as long as you know x is actually AnyView or a subclass. You loose that freedom on the Swift side where for your own safety you need to be extremely explicit. You even have to tell it if the return can be nil. In all honesty I do not like that, but here you can not solve Swift's strictness by doing it differently on the Objective-C side. I have added a suggestion to my answer that may help... – skaak Aug 15 '20 at 16:23
  • Note how we are getting more and more specific. The original answer I mentioned did it as generically as possible with a category on NSObject and a return type of ```id``` - which means anything really and will work for anything in Objective-C. Now we narrowed it down to UIView and later even to MyView. In Objective-C we do not need to be as specific but on Swift side we have to be more or less exact. This is a pain in the ... neck but on the box it reads as if Swift is doing it for your own good ... ahem ... safety. Sorry for all these comments but it is one thing about Swift I don't like. – skaak Aug 15 '20 at 16:39
  • Also note, in original answer, you'd get an ```id``` back but one of the arguments would be the class you are looking for. Easy in Objective-C but maybe some of those Swift examples in the original answer can also help you here? – skaak Aug 15 '20 at 17:05