Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Include an extension for a class only if iOS11 is available

I am trying to extend a class written in Obj-C and include an extension written in Swift that makes it conform to the UIDropInteractionDelegate, like so:

@available(iOS 11.0, *)
extension NoteEditViewController: UIDropInteractionDelegate {
    @available(iOS 11.0, *)
    public func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
        let operation: UIDropOperation
        if session.localDragSession == nil {
            operation = .forbidden
        } else {
            // If a local drag session exists, we only want to move an
            // existing item in the pin board to a different location.
            operation = .forbidden
        }
        return UIDropProposal(operation: operation)
    }

    @objc(setupDropInteractions)
    @available(iOS 11.0, *)
    func setupDropInteractions() {
        // Add drop interaction
        self.view.addInteraction(UIDropInteraction(delegate: self))
    }
}

My problem is that Project_Name-Swift.h file contains the following code that will not compile:

@class UIDropInteraction;
@protocol UIDropSession;
@class UIDropProposal;

// This line is causing the issue saying "'UIDropInteractionDelegate' is partial: introduced in iOS 11.0"
@interface NoteEditViewController (SWIFT_EXTENSION(Bloomberg_Professional)) <UIDropInteractionDelegate>
- (UIDropProposal * _Nonnull)dropInteraction:(UIDropInteraction * _Nonnull)interaction sessionDidUpdate:(id <UIDropSession> _Nonnull)session SWIFT_WARN_UNUSED_RESULT SWIFT_AVAILABILITY(ios,introduced=11.0);
- (void)setupDropInteractions SWIFT_AVAILABILITY(ios,introduced=11.0);
@end

The compiler is complaining that the interface in that file is partial.

'UIDropInteractionDelegate' is partial: introduced in iOS 11.0

I assumed that including the @available(iOS 11.0, *) would generate a SWIFT_AVAILABILITY(ios,introduced=11.0) that would encapsulate the entire interface but I was wrong.

Is there a way to fix this?

UPDATE


I implemented a toy example.

Here is the toy ViewController:

enter image description here

Here is the swift extension:

enter image description here

And here is the generated dnd_toy-Swift.h file.

enter image description here

You were right that it is simply a warning but my problem is that we must treat all warnings as errors in our project.

Any ideas on how to get rid of this warning from here?

like image 756
Makaronodentro Avatar asked Jul 07 '17 15:07

Makaronodentro


People also ask

Can you add a stored property to a type by using an extension?

Extensions cannot contain stored properties. If you need to add a property to a native component, you must create your own button inheriting from UIButton.

How do I create a class extension in Swift?

Creating an extension in Swift Creating extensions is similar to creating named types in Swift. When creating an extension, you add the word extension before the name. extension SomeNamedType { // Extending SomeNamedType, and adding new // functionality to it. }

What is the difference between category VS extension?

Category and extension both are basically made to handle large code base, but category is a way to extend class API in multiple source files while extension is a way to add required methods outside the main interface file.


2 Answers

You've done everything correctly, and the complaint is accurate if not extremely obtuse.

Basically, since you've (correctly) marked that function as available in iOS 11, the compiler will warn you about the use of it in any code that is targeting something < iOS 11.

So you can make the complaint go away by setting your deployment target to iOS 11, which means users of iOS 10 simply won't be allowed to install it. Or, you can use the new (to obj-c) @available construct to guard against the use of the API.

if (@available(iOS 11, *)) {
    [self setupDropInteractions];
}

This construct isn't supported by Xcode 8 since it's a recent back port of the #available construct provided by Swift.

Update

I'm clarifying where I'm coming from. It seems I haven't been able to reproduce the situation that the asker is experiencing, so I'm demonstrating what I have been able to do.

I can produce 2 different compiler warnings that are similar but it seems not identical to the original question.

Here is my generated objc interface from my_project-Swift.h:

@interface NoteEditViewController (SWIFT_EXTENSION(conditional_class_declaration)) <UIDropInteractionDelegate>
    - (UIDropProposal * _Nonnull)dropInteraction:(UIDropInteraction * _Nonnull)interaction sessionDidUpdate:(id <UIDropSession> _Nonnull)session SWIFT_WARN_UNUSED_RESULT SWIFT_AVAILABILITY(ios,introduced=11.0);
    - (void)setupDropInteractions SWIFT_AVAILABILITY(ios,introduced=11.0);
@end

Issue 1: Declaring and objc property to conform to the protocol

enter image description here

Issue 2: Using a method declared in the extension

enter image description here

With more information to reproduce the compilation error, I'll be able to help more.

Update 2: Hopefully the last

I found that the discrepancies in behavior between us was due to the fact that my project was created with Xcode 8.3 then migrated to 9. There seems to be a difference in build settings after these things happen. The setting in question is CLANG_WARN_UNGUARDED_AVAILABILITY, which I think is new for Xcode 9.

During migration the project ends up like this:

  • This maps to the term YES in the .pbxproject file enter image description here

After new project creation:

  • This maps to the term YES_AGGRESSIVE is the .pbxproject file enter image description here

This setting is discussed in WWDC2017 - What's new in LLVM, but nothing they say would suggest this minor behavioral difference. I'm guessing that it is a bug with clang and how it's handling the difference between the two settings (but I'd welcome other input). As I'm sure you've figured out, this code runs fine on iOS 10. Also, if you change the setting to simply "Yes", you will still get the correct warnings about iOS 11 APIs.

like image 193
allenh Avatar answered Oct 16 '22 09:10

allenh


In ObjC, adding API_AVAILABLE(ios(11.0)) to end of the function definition will suppress the warning. Like this:

- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0))
{
     ... function implementation ...
}
like image 38
Farhad Malekpour Avatar answered Oct 16 '22 09:10

Farhad Malekpour