SPM library with Objective-C NSURL category not imported into Swift URL?
Asked Answered
E

2

2

I have created a SPM package with a bunch of old Objective-C code (I want to convert some old code "Objective-C code with a big bridging header" into "Objective-C in some SPM packages") For most of the cases it works OK except for Objective-C categories like:

@import Foundation;

@interface NSURL (Additions)

@property (copy, readonly) NSDictionary<NSString *, NSString *> *queryParams;

@end

Problem: Property queryParams is NOT available in Swift on URL types but only on NSURL types. This looks odd to me because when declaring the same category in an App and using the bridging header file, then queryParams is also usable from URL type too.

Tests/URLUtilsTests/URLUtilsTestsSwift.swift:18:31: error: value of type 'URL' has no member 'queryParams'
        let queryParams = url.queryParams
                          ~~~ ^~~~~~~~~~~
error: fatalError

Question: What do I need to have queryParams in both NSURL and URL while using SPM?

I have created a repo in GitHub with reproducing problem

This is a screenshot of my entire package:

enter image description here

Essie answered 8/5, 2023 at 15:21 Comment(0)
E
1

I asked the same question in Swift forums and I got the reason why this is not working.

NSURL and URL are not the same type. The first is a class, the second a struct. ObjC categories or Swift extensions to one do not apply to the other.

There's a runtime "magic" that ties them together, though. It is the URL conformance to ReferenceConvertible 5, and much probably some non-public tricks. But this does not make them the same type.

So the only solution is to have a special target for swift code.

import URLUtils
extension URL {
    var queryParams: [String: String] {
       (self as NSURL).queryParams
    }
}

I have updated my original repository source code to and it works ok now!.

Essie answered 14/6, 2023 at 0:12 Comment(0)
B
1

One possible solution would be to create extension(s) to handle properties that don't exist on Swift types based on the Objective-C equivalents.

In your tests include a .swift extension:

// URL+QueryParams.swift

import Foundation

extension URL {
    var queryParams: [String: String] {
        var urlDict = [String: String]()
        guard let query = self.query else { return urlDict }
        let subArray = query.components(separatedBy: "&")
        for element in subArray {
            let urlArray = element.components(separatedBy: "=")
            if urlArray.count < 2 { continue }
            urlDict[urlArray[0]] = urlArray[1]
        }
        return urlDict
    }
}
Breland answered 9/5, 2023 at 4:42 Comment(1)
Query params don't always have anything to the right of =, which would make this crashSultan
E
1

I asked the same question in Swift forums and I got the reason why this is not working.

NSURL and URL are not the same type. The first is a class, the second a struct. ObjC categories or Swift extensions to one do not apply to the other.

There's a runtime "magic" that ties them together, though. It is the URL conformance to ReferenceConvertible 5, and much probably some non-public tricks. But this does not make them the same type.

So the only solution is to have a special target for swift code.

import URLUtils
extension URL {
    var queryParams: [String: String] {
       (self as NSURL).queryParams
    }
}

I have updated my original repository source code to and it works ok now!.

Essie answered 14/6, 2023 at 0:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.