Include an extension for a class only if iOS11 is available
Asked Answered
W

3

8

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?

Weidman answered 7/7, 2017 at 15:22 Comment(0)
W
4

Xcode 9 beta 4 will build, if @available is added to each method. The build still fails if @available is added only at the extension level.

Wintry answered 28/7, 2017 at 0:18 Comment(0)
H
8

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.

Hypersensitive answered 10/7, 2017 at 19:2 Comment(5)
I am guarding against any uses off the API in my obj-c code, yet the error persists. As mentioned above the interface generated for the extension in the Project_Name-Swift.h file does not guard against the version and thus the project will not compile. I need the build to be compatible with iOS 10 hence your first solution is not an option.Weidman
@Weidman What mechanism are you using to guard against the usage? You are saying this is an 'error', are you getting a runtime error or is it just the compiler warning? I provided two solutions for removing the warning, and 1 of them (using if (@available(iOS 11, *)) is FULLY compatible with iOS 10, but requires Xcode 9. If you need to compile with Xcode 8, that is a different matter.Hypersensitive
Hey, thank you for looking into it so much! I am using Xcode 9 Beta-2 at the moment. I am not receiving any warnings inside my ViewController as I have included the @available guards and the availability macro in the interface. I am getting a 'Semantic Issue' during compilation. The moment "-Swift.h" file is imported by another file I am getting the warning you get in your interface as an error, and it is pointing to the auto-generated obj-c interface in "-Swift.h"Weidman
@Weidman If you can reproduce the exact issue in a sample project or provide more details on the error, I may be able to help. Also, upgrade to beta 3 just to be sure.Hypersensitive
@Weidman Nvm, I was able to reproduce it, checking now.Hypersensitive
P
8

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 ...
}
Palenque answered 10/10, 2017 at 6:25 Comment(1)
Is there a way to check for iOS 11 not available?Labionasal
W
4

Xcode 9 beta 4 will build, if @available is added to each method. The build still fails if @available is added only at the extension level.

Wintry answered 28/7, 2017 at 0:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.