Swift should allow for suppression of warnings, especially those that come from Objective-C

I’ve ended up editing SDK header files to silence some deprecation warnings.
Now there’s one less distracting warning you can do nothing about.

I know, it’s dirty. But if there’s no replacement API available in the current SDK, it should be safe. Once a new version of Xcode comes out, the change will get overwritten and you will see the warning again. Then you can test the new SDK and OS to make sure the deprecated API is still available and did not get a proper replacement.

Can’t think of any downsides in this case.

In cases like yours (it took a lot of scrolling to work out that you're talking about SMJobSubmit :-) I generally resolve this conundrum by writing a small Objective-C wrapper around the function and then suppressing the warning on the Objective-C side. This also gives you the opportunity to expose a nicer API. For example, with this code:

QSMJob wrapper
@implementation QSMJob

+ (BOOL)submitJob:(NSDictionary *)job authorization:(AuthorizationRef)authorization error:(__autoreleasing NSError **)error {
    CFErrorRef cfError;
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    BOOL result = SMJobSubmit(kSMDomainSystemLaunchd, (__bridge CFDictionaryRef) job, authorization, &cfError) != false;
    #pragma clang diagnostic pop
    if (!result) {
        if (error != NULL) {
            assert(cfError != NULL);
            *error = CFAutorelease(cfError);
        }
    }
    return result;
}

@end

I get to call this:

class func submitJob(_ job: [AnyHashable:Any], authorization: AuthorizationRef) throws

rather than this:

func SMJobSubmit(_ domain: CFString!, _ job: CFDictionary, _ auth: AuthorizationRef!, _ outError: UnsafeMutablePointer<Unmanaged<CFError>?>!) -> Bool

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

8 Likes

I had already devised a nice wrapper, but in Swift and the warning was the only issue.

Whenever I have some obscure issue with few search results, I often stumble on your replies on Apple forums. Thank you.

2 Likes

As everybody else for more than 2 decades :wink:

8 Likes

To add to this discussion: sometimes a warning is not actually valid and just confuses matters. An example case could be because you implemented something for performance reasons in a non-standard way and it would be very nice to be able to suppress such a warning.

Example:

When implementing a LinkedList in a performant manner it is beneficial to avoid letting ARC do its magic and manually retain/release the nodes instead. I have this in my implementation:

private class Node<Element> {
    var value: Element
    
    // Usage of unowned is required to avoid running into troubles when deallocating a big list: will recursively call dealloc otherwise leading to a BAD_ACCESS
    unowned var next: Node<Element>?
    unowned var previous: Node<Element>?
    
    init(_ value: Element) {
        self.value = value
        // Perform a manual retain call on init
        self.retain()
    }
    
    func retain() {
        _ = Unmanaged.passUnretained(self).retain()
    }
    
    func release() {
        _ = Unmanaged.passUnretained(self).release()
    }
    
    func autorelease() {
        _ = Unmanaged.passUnretained(self).autorelease()
    }
}

Now I defined a function insert as follows. The warning which is displayed is not actually valid. I'm doing this on purpose to avoid unneeded retain/release calls by ARC, and the Node is retained in the initializer.

private final class LinkedListStorage<Element> {
    
    unowned var head: Node<Element>?
    unowned var tail: Node<Element>?
    var count = 0
    
    ....

    func insert(_ element: Element, relativeTo: Node<Element>?, before: Bool) {
        //WARNING: Instance will be immediately deallocated because variable 'node' is 'unowned'
        unowned let node = Node(element)
        if let anchor = relativeTo {
            if before {
                node.next = anchor
                node.previous = anchor.previous
                anchor.previous?.next = node
                anchor.previous = node
            } else {
                node.previous = anchor
                node.next = anchor.next
                anchor.next?.previous = node
                anchor.next = node
            }
        }
        
        if node.next == nil {
            tail = node
        }
        if node.previous == nil {
            head = node
        }
        count += 1
    }
}
1 Like

This is the kind of code that voids Swift’s warranty. If you want to manage memory manually, just make Node a struct and have next and previous be UnsafeMutablePointer<Node>. It’s more honest with the optimizer and, really, it’s more honest with yourself.

11 Likes

Instead of suppressing deprecation warnings to fix your build because you have warnings as errors enabled, disable warnings as errors. :exploding_head:

1 Like

I’m also a fan of some annotation in my source code that can hide individual warnings. Deprecation warnings seem to be the only ones I’ve had issue with, but I’m split on if this annotation should be deprecation specific or cover all types of warnings. I’ve been turning off warnings as errors with each SDK bump to see what new deprecation warnings arrive and bumping my deployment target. But after fixing the warnings that I can, I lower the deployment target back down (on frameworks) to silence the warnings. This is so hackey, but the only way to hide these. I’d love to fix my code but have no way to. Need to use MD5 in a non-cripto context for legacy reasons? Or how about use OpenGL on iOS 12+ (can’t switch to metal because not all iOS 12 devices have full metal support dispute OpenGL being deprecated on 12)? Allowing warnings to persist in a project is really just a recipe to not notice new warnings when you do accidentally do something wrong.

4 Likes

Does Insecure.MD5.hash(data: data) from CryptoKit help this (specific) situation? It won't output warnings.

2 Likes

I don't know, but I shouldn't have to import a 3rd party library to work around a deprecation warning, when the SDK has a working function to do the same thing.

I am referring to CryptoKit, the Apple SDK library, the modern substitute for CommonCrypto.

1 Like

Didn't know that CryptoKit existed, seems kinda crazy to mark MD5 as deprecated in one framework and not in another. If you intend to move it, then the deprecation warning's message should say so.

I still feel the point stands, that we need some way to hide individual warnings. I liked the idea of the annotation hiding the warning for a single Swift version. This way you will see the warning on a regular basis, and will need to rehide it to confirm you are ok with it.

// Silences a warning, but only in Swift 5.1
@silenceWarning(Swift5_1)

I'd also be good if we tie it to SDK version instead, but can see how that wouldn't work well for Linux use.

I guess the thinking is that the Insecure namespace is enough in CryptoKit land.

I do agree with the wider point. CC_MD5 should probably not have been annotated as deprecated; it's not really the right tool for the job. Swift's principled stance is theoretically ideal, but frameworks — including Apple's — often use warnings when they probably shouldn't and having no way to override that is annoying.

3 Likes

Instead of suppressing deprecation warnings to fix your build because you have warnings as errors enabled, disable warnings as errors. :exploding_head:

Uhmm, this isn't part of the problem. And furthermore this isn't just about silencing deprecation.

My issue is that there are certain things that still don't work in the simulator. Camera, StoreKit, many others.
This bit of code is common.

        #if targetEnvironment(simulator)
        return
        #endif

So now the warning is "Will never be executed", but that's exactly what we want.... Why can't I have a way to hide that.

2 Likes

Since this isn’t a useful diagnostic in this scenario, it’s reasonable to file a bug so that it’s not shown under these circumstances.

3 Likes

I think it would be beneficial to instead give us a way to silence warnings instead of hard-coding niche warnings away like this.

3 Likes

Has this gone anywhere?

I don't want to globally disable warnings. I want to see all deprecations. But I want to silence specific ones.

You can argue all you want that the root cause should be fixed, even if it means editing dependencies and updating your whole codebase. But how am I supposed to change UIKit?

Here's the relevant bit of code:

private func prepareForPresenting(_ viewController: UIViewController) {
    guard #available(iOS 13, *),
        viewController is UIDocumentMenuViewController,
        DisplayUtils.phone else { return }

    guard let webViewController = viewControllers.last as? WebViewModuleViewController,
        let popoverPresentationController = viewController.popoverPresentationController else { return 
    }

    // Set sourceView and sourceRect for UIDocumentMenuViewController presenter from a WebView
    // Based on: https://stackoverflow.com/questions/58164583
    popoverPresentationController.sourceView = webViewController.webView
    popoverPresentationController.sourceRect = CGRect(origin: webViewController.lastWebViewTapPosition, size: .zero)
}

Yes, UIDocumentMenuViewController got deprecated in iOS 11. Yet WKWebView still uses it under the hood, even as of iOS 14, and is causing a crash that is 100% reproducible with the new iOS 13 style modal sheet presentation.

So what are my options?

  • Globally disable all deprecations. Amazing, now I don't see any deprecation warnings.
  • Get used to always seeing 1 warning in the Xcode build output. Amazing. Broken window effect anyone?
  • Remove it from my code at let my users enjoy the crashes?
6 Likes

Report the bug so that it's fixed for everyone.

This is of course an important part of any response, but it’s also extremely unhelpful in addressing the concerns raised.

Even if this does get fixed, this is out in the world within existing iOS versions, and needs to be handled by anyone presenting web views on these iOS versions which happens to invoke this screen, even if it does get fixed in later releases.

I believe this points out a flaw in the current deprecation warning system, that you still may require to use and work with deprecated types, because any framework or library, including other system libraries, may still be using them and you need to work with them regardless of the “best practice”.

It would be nice to have this addressed rather than avoiding and ignoring the real-world issue.

13 Likes

Great idea man! Then I will only have to see the deprecation warning for another 2.5 years until we drop iOS 14 support.

3 Likes