Consider:
A Swift framework called FrameworkA that defines the type Thing.
A Swift framework called FrameworkB that also defines the type Thing and the type FrameworkA.
An app that imports both frameworks in the same Swift file.
How do I reference FrameworkA.Thing in said file? The following line fails with Thing is not a member of FrameworkA.
let t : FrameworkA.Thing? = nil
This appears to be a Swift bug. As a workaround, you can create a new Swift file in the app that imports only FrameworkA and defines a typealias for Thing:
import FrameworkA
typealias ThingA = Thing
Then in the file that needs to import both frameworks, you use ThingA instead of FrameworkA.Thing.
Related
I just converted a git repository to Swift Package Manager (spm), all good, it compiles and I can import it in the project.
What is not good is that the project doesn't compile, is not seeing the protocol. My lib is a single generic class and i'm supposed to create an enum that implements the protocol then use the enum as a generic of the class.
enum LocalPreferences: String, RCPreferencesProtocol {
I get: Use of undeclared type RCPreferencesProtocol
Then I get further errors when trying to use the enum but i think this are only because the enum had an error:
private let localPreferences = RCPreferences<LocalPreferences>()
I get: Cannot specialize a non-generic definition
Anybody had this problem and fixed it?
Here is reference to the library is the lib for reference.
I made a stupid mistake, didn't declared the protocol and class public in the package. Strange though that the unit tests of the package passed instead giving the same error.
In c# when pulling in a library that has a lot of name collisions with existing code, there's a way to alias the import so you don't need to fully clarify the namespace for each use. eg:
using MyCompany.MyLibrary.Model as MMM
then you could do
MMM.MyObject
instead of
MyCompany.MyLibrary.Model.MyObject
With the recent update to swift 3.0, I've found some of my model objects are now colliding with the Foundation types, and I've been forced to prefix things that used to have an NS prefix in the class name with Foundation.classname. It would be great if I could type alias the import of my model library much like the c# example given above. Is this possible in swift 3.0? If not is there another strategy to avoid name collisions that would result in having to write the framework name in front of each type? I'm considering going back to prefixing my class names like we did in obj-c, but I'm trying to explore my options before I do that.
Update 2021 for Swift 5;
No, but you can import all and alias (in separate file), read below for more details.
Generally
You can import a particular entity as well as the entire module:
import struct SomeModule.SomeStruct
import class SomeModule.SomeClass
import func SomeModule.someFunc
See the full list of "importable" entity types in the import-kind rule of Swift grammar.
Then you can create a typealias:
typealias SMSomeStruct = SomeModule.SomeStruct
And, as of Swift 3, there is no import declaration combined with aliasing.
Considering the Collisions with Foundation Entities
Say, you have a class SomeModule.NumberFormatter.
It is enough to create two typealiases in a separate Swift file (in an importing project) to prevent collisions:
import Foundation
import SomeModule
typealias NumberFormatter = Foundation.NumberFormatter
typealias SMNumberFormatter = SomeModule.NumberFormatter
It's not even with Swift 5 possible to directly alias an import-statement!?
But fortunately, I could workaround it with something like:
import typealias MyModule.MyInnerClass
I mean, if we are the module's developer, simply move the alias into the module, like:
public typealias MyInnerClass = MyClass.MyInnerClass
public class MyClass {
public class MyInnerClass {
// Some cool logic here...
}
}
In my project I have Swift extensions over Objective C enums, which worked brilliantly in Xcode 7.2.
But with Xcode 7.3 it fails with "undeclared type" in the Swift file where I extend the Obj C enum.
I've built a sample project and it compiles and works well, but I can't make the existing project accept the extension over the Obj C enum.
UPDATE:
After reinstalling Xcode 7.2 I can confirm that the project compiles and builds successfully.
After it worked in Xcode 7.2, I tried launching this project again in Xcode 7.3 and again the same issue -> the Swift extension over Obj C enums can't be build.
After cleaning and deleting the derived data in Xcode 7.3 I receive also the -Swift.h header missing error because the Swift classes haven't been compiled so a header wasn't created yet.
Code explanation:
My Obj C enum inside "FriendRequestResult.h":
typedef NS_ENUM(NSInteger, FriendStatus)
{
FriendStatusRequestedByUser = 1,
FriendStatusRequestedByOtherUser,
FriendStatusFriends,
FriendStatusBlocked,
FriendStatusNone,
FriendStatusError,
};
Now, as expected in my AppName-Bridging-Header.h I have:
#import "FriendRequestResult.h"
Then, I have the swift extension over the FriendStatus which builds in Xcode 7.2, but fails with "use of undeclared type" in Xcode 7.3:
extension FriendStatus
{
init(stringValue : String?)
{
if let stringValue = stringValue
{
switch stringValue
{
case "REQUESTED_BY_USER": self = .RequestedByUser
case "REQUESTED_BY_OTHER": self = .RequestedByOtherUser
case "FRIENDS": self = .Friends
case "BLOCKED": self = .Blocked
default: self = .None
}
}
else
{
self = .None
}
}
}
Actually this extension over the enum has also some other helper functions, but that should not change the problem in any way.
Of course if the extension of the enum gives the undeclared type, then using this type fails everywhere in the Swift code with the same "undeclared things". Basically the enum is not visible at all for the Swift part of the project, even though the import is made in the bridging header.
This question was substantially edited from its first version.
Solution 1:
Moved the enum in a seaparate header file.
My enum declaration was in the same header as a class header and specifically it was between the #interface and #end of that class.
In Xcode 7.2 it was creating no issues and the parsing of the header was successful, while in Xcode 7.3 they probably optimised something and changed the way it's parsed, so it was seeing my class, but not the enum inside it [maybe it declares it as private if it's inside a class declaration]
Solution 2: Moved the enum declaration outside the #interface #end scope.
To answer your question: "Can I somehow force Xcode to generate the Swift header first? Or can I force Xcode to believe that my enums exist somewhere and that it should validate my extensions?"
I found 2 ways to order file compilation.
Using a target and making it a dependency of your project.
Opening the project.pbxproj file and editing the list manually.
Depending on the level of risk and complexity you are ready to tackle, pick one or the other.
I just tried extending an ENUM and things are working fine here. One issue I had in the past was understanding the name stripping convention between Obj-C and Swift but this doesn't look like the issue you are running into.
The other issue I encounter invariably is maintaining the ##$% Bridging-Header.h file all the time. Are you positive this is up-to-date?
I want to declare an Objective-C method with a custom Swift class as one of it's arguments, like this:
+ (void)doCalculationsWithACustomSwiftObject:(CustomSwiftClass *)swiftObject andADictionanary:(NSDictionary *)ordinaryDictionary;
The aim is to call this method from Swift, pass an instance of the custom class "CustomSwiftClass.swift" so that the Objective-C code can access its properties. Is it possible, and if so, how do I declare the "CustomSwiftClass" to make the compiler accept it?
A couple of thoughts:
To make Swift class accessible to your Objective-C code, you have to import the system generated header. As the documentation says:
When you import Swift code into Objective-C, you rely on an Xcode-generated header file to expose those files to Objective-C. This automatically generated file is an Objective-C header that declares the Swift interfaces in your target. It can be thought of as an umbrella header for your Swift code. The name of this header is your product module name followed by adding "-Swift.h". (You’ll learn more about the product module name later, in Naming Your Product Module.)
For example, your Objective-C file would import this generated header:
#import "MyApp-Swift.h"
For more information, see Importing Swift into Objective-C section of the Using Swift with Cocoa and Objective-C: Mix and Match
If you're not seeing the xxx-Swift.h file, double check your target settings:
(Note, I searched for "swift" in the settings to narrow down the results.)
As you'll see, the following are all set:
"Install Objective-C Compatibility Header" is set to "Yes";
The "Objective-C Generated Interface Header Name" is set (and matches what I imported into my Objective-C code);
The "Objective-C Bridging Header" is also specified.
Note, the Objective-C bridging header is ostensibly solely for the purpose of making the Objective-C classes available to the Swift code (and you're currently trying to accomplish the converse), but I notice that this affects what code is generated in the compatibility header, too. So make sure you have a bridging header, too.
Of course, make sure your Swift file is included in the list of compile sources:
And make sure the class is subclassed from NSObject:
import UIKit
class MyObject: NSObject {
var text: String?
var value: Int = 0
}
Note, some Swift types are not available to Objective-C code. For example, Objective-C cannot not use some optional types (e.g. an optional type such as Int? will not work, whereas an optional class type of String? will). So if you are seeing the class, but not some of the properties, make sure they're Objective-C compatible.
But I gather from your question that you're having more fundamental problems, that you're not finding the -Swift.h header at all. But once you solve that, this is a consideration for individual properties.
I setup a podspec for my repo (framework). In my repo, there are some swift classes, I would like these swift classes be availble to some objC classes in the same repo.
I know xcode will automatically generate a header file: product-Swift.h file, so that object-c class can use swift classes by importing this header file.
my question is that
how do I setup podspec file so that this swift header file can be generated and objC class can import it ?
in my podspec file, I enable module definition and generate interface build header file for swift. but I can not get my objC class compiled since the product-Swift.h file can not be found.
s.xcconfig = { 'DEFINES_MODULE ' => 'YES', 'SWIFT_OBJC_INTERFACE_HEADER_NAME' => 'Product-Swift.h' }
any ideas are welcomed, thanks in advance.
you can do this by adding #obcj prefix to you class name. btw, you'll have to re-build your project before objective c will know the class
#objc class myClass{
//this class will be available in Objective c
}