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

4 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:

4 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
    }
}

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.

5 Likes

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

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.

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

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.

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.

Terms of Service

Privacy Policy

Cookie Policy