Swift - Can't use Namespace when Framework/Module with class with same name
Asked Answered
N

2

12

Environment:

Swift >=2.3, Swift Framework in a Swift app

SetUp:

  • A Swift dynamic framework with module name, let's say APIService.framework.
  • It has a public class APIService.swift and it has static functions.
  • It has another public class Item.swift.

  • A swift application that usage APIService.framework

  • App can work with the framework like APIService.function()
  • App also has a class Item.swift

If App needs to refer Item class of framework it has to do APIService.Item but since APIService is a class inside the framework, compiler always try to look for a property inside APIService class rather than in the APIService Module, hence throws an error saying

'Item' is not a member type of 'APIService'

Possible Solutions:

  • Change the framework's Module name to something other than class name.
  • Change the class name to something other than the Module Name.
  • Put a "Item" static property inside APIService class that points to
    Item class in framework.

All these are just workarounds, the real issue remains that compiler is not able to differentiate between Module name and class name. Do we have anything in Swift by which I can say "Don't look into the ModuleName.swift, instead look into the whole Module" ?

Niersteiner answered 24/12, 2016 at 7:14 Comment(0)
D
2

It's old problem with symbol name collisions in Swift because the compiler resolves names in the following priority order:

  1. Local Declarations
  2. Imported Declarations
  3. Imported Modules

Your app's Item class name overrides the imported one from APIService module and APIService class name overrides its module name in your case so that you can't access to APIService.Item anymore.

The only solution is importing the framework to a clear namespace (module) without collisions, typealiasing and then reimport it into your app and to do that you can make a wrapper framework and link your third-party library in i.e.:

Your App -> Wrapper Framework(typealias) -> Target Framework.

Let's APIService framework has implementation:

APIService.swift

public class APIService {
    public static func test() {
        print("APIService")
    }
}

public class Item {
    public static func test() {
        print("APIService.Item")
    }
}

Make Dependencies framework, link it with APIService framework and add this file:

Dependencies.swift

import APIService

public typealias APIService_Item = Item

Now you can link Dependencies framework into your app (APIService is linked automatically) and import the libraries to your code:

App.swift

import Dependencies
import APIService

class Item {
    static func test() {
        print("App.Item")
    }
}

...

Item.test() // Prints: App.Item
APIService.test() // Prints: APIService
APIService_Item.test() // Prints: APIService.Item

Thus as you can see from the test we've got access to APIService.Item class.

Developer answered 23/2, 2023 at 22:48 Comment(0)
A
2

Based on this answer, it looks like you can directly import a single type from a module to get around this.

import class APIService.Item

Then you can refer to Item directly without confusion.

The class, protocol, struct, enum and func keywords (at least) work in this situation.

Allbee answered 30/5, 2017 at 1:55 Comment(1)
Doesn't work in my case. Xcode 14 🤷🏻‍♂️ Error Item is not a member type of class APIService.Item. Still the question is "Do we have anything in Swift by which I can say "Don't look into the ModuleName.swift, instead look into the whole Module""Vert
D
2

It's old problem with symbol name collisions in Swift because the compiler resolves names in the following priority order:

  1. Local Declarations
  2. Imported Declarations
  3. Imported Modules

Your app's Item class name overrides the imported one from APIService module and APIService class name overrides its module name in your case so that you can't access to APIService.Item anymore.

The only solution is importing the framework to a clear namespace (module) without collisions, typealiasing and then reimport it into your app and to do that you can make a wrapper framework and link your third-party library in i.e.:

Your App -> Wrapper Framework(typealias) -> Target Framework.

Let's APIService framework has implementation:

APIService.swift

public class APIService {
    public static func test() {
        print("APIService")
    }
}

public class Item {
    public static func test() {
        print("APIService.Item")
    }
}

Make Dependencies framework, link it with APIService framework and add this file:

Dependencies.swift

import APIService

public typealias APIService_Item = Item

Now you can link Dependencies framework into your app (APIService is linked automatically) and import the libraries to your code:

App.swift

import Dependencies
import APIService

class Item {
    static func test() {
        print("App.Item")
    }
}

...

Item.test() // Prints: App.Item
APIService.test() // Prints: APIService
APIService_Item.test() // Prints: APIService.Item

Thus as you can see from the test we've got access to APIService.Item class.

Developer answered 23/2, 2023 at 22:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.