How to make a Swift framework submodule really private? - swift

I've found another question which brings more details regarding the problem and possible solutions. It seems like there is a known bug which is a subject for future improvements.
Objective C classes within an iOS Swift-based dynamic framework
I'm developing a framework in Swift and I'm using some Objective-C code inside the framework. So far my module map looks like this:
framework module MyModule {
umbrella header "MyModule-umbrella.h"
export *
explicit module Private {
header "MyTools.h"
}
}
My concern is that all the APIs from MyTools.h are visible from outside the framework: for example, if you install the framework using Cocoapods, then you import MyModule into your application (not MyModule.Private), you are able to access MyTools.h which is not desirable and redundant. Is there any way to make MyTools invisible from outside the framework?
PS. I use Cocoapods to distribute the framework, here is my podspec (the most significant part):
s.module_map = 'Pod/MyModule.modulemap'
s.frameworks = 'CoreData', 'CoreTelephony', 'SystemConfiguration'
s.resources = 'Pod/Classes/MessageStorage/*.xcdatamodeld'
s.public_header_files = 'Pod/Classes/**/*.h'
s.private_header_files = 'Pod/Classes/MyTools/**/*.h'
s.source_files = 'Pod/Classes/**/*.{h,m,swift}'
PSS. My umbrella header does not import MyTools.h
PSSS. Just tried to exclude the header from the main module:
framework module MyModule {
umbrella header "MyModule-umbrella.h"
export *
exclude header "MyTools.h"
explicit module Private {
header "MyTools.h"
}
}
No luck.

I found another question which brings more details regarding the problem and possible solutions (which don't work though). It seems like there is a known bug which is a subject for future improvements.
Objective C classes within an iOS Swift-based dynamic framework

I had exactly the same problems recently. The quick answer is you can't :) Even if you declare "private" modulemap, it can be always imported by your framework users. Please note, that usually, it is not a concern, especially with open source. You just say "this is an internal module, don't use it".
But (there is always but) - you can have behavior, that effectively works the same - allows you to use your Objective-C classes withing same framework target, without making them public. It works in closed source setup, I'm not 100% sure how would it behave with pods.
The case a bit too complex to paste everything here. I'm adding a link to my article about the topic, maybe it will help you. But speaking honestly - it might be a bit of overhead in your setup.
Creating Swift framework with private Objective-C members. The Good, the Bad, and the Ugly
Github example project

Related

Can I use library keyword in personal project but not in library

I am using Flutter. As you are aware, Flutter doesn't have protected keyword to make classes package private. But interestingly, the same could be achieved using library, part, part of keywords once I name widgets starting with underscore. But, I am a bit worried about performance issues.
Question: Is it ok to use library/part/part of keywords inside ordinary projects?
public classes inside lib/src are considered package private. Only classes in lib are truly public, so are files in lib/src when they get exported by a file in lib.
enchilada/
lib/
enchilada.dart <-- public
src/
beans.dart <-- package private unless exported
While technically you can access everything in lib/src, you get a warning when you use implementation files.
Don't use part
The part keyword can not be used to hide code, it inherits the same visibility as the library. All it does is splitting a file into multiple files. It should only be used for code generation these days https://stackoverflow.com/a/27764138/669294
Note: You may have heard of the part directive, which allows you to split a library into multiple Dart files. We recommend that you avoid using part and create mini libraries instead.
source
Performance
Visibility doesn't affect performance in any way.
Naming your entity starting with an underscore (for example _test) should make it private.
Libraries not only provide APIs, but are a unit of privacy: identifiers that start with an underscore (_) are visible only inside the library. Every Dart app is a library, even if it doesn’t use a library directive.
Source

Import third party C library into swift causes error "Include of non-modular header inside framework module"

This question is a continuation of a previous one I'm currently migrating this (https://github.com/emilwojtaszek/leveldb-swift) library from swift 2 to swift 3/4. Here is the link to my fork https://github.com/lu4/leveldb-swift/tree/MigrationFromSwift2ToSwift3 (please note that the target branch is MigrationFromSwift2ToSwift3)
I was able to resolve (with many thanks to #Ruslan Serebriakov) all of the issues with initial code base and check that the code is running.
However after trying to update LevelDB C code to latest master I got new type of error which I don't understand how to resolve:
Include of non-modular header inside framework module 'LevelDB.c': '/Path/to/Project/leveldb-swift-migration/vendor/leveldb/include/leveldb/export.h'
I've did some research on the internet but the issues described there seem non-related with one I've stumbled on. Here is an image of the issue
Any help is appreciated, thank you in advance!
I'm never 100% certain with mixed language frameworks. But an error like this happens in Swift projects when:
since you cannot use a Bridging Header in frameworks,
you #import a C header in the Foo-Framework.h to expose it so the Swift code, and
the header is not itself marked "Public" to the target.
"Non-modular" seems to indicate "not part of the published module interface". At least with Swift--C mixes, you can only combine both through making the C headers public; no way to import private header files there, which is weird.
Give it a shot: Since you are obviously importing the file in non-Swift code, try to locate the export.h header file in your Xcode project, open the File inspector (⌘⌥1), and ensure public visibility in the framework target:
This issue is because the SDK u are importing is not modular or u can say modulemap file is missing. So make sure modulemap file should be available inside the framework folder. Also make sure that all public headers are listed explicitly in the modulemap. This issue will be resolve 100% if module map file will be include in the third party framework.

How do you create public extensions, in a shared framework, for XCTest?

For example, I never use the description of XCTestCase.expectation, so I'd like to use a function to provide a default for it, and make it clear via naming that I'm initializing the expectation, as you can't really use an initializer for XCTestExpectation. But if the extension is not in a test target, then it can't compile:
Cannot load underlying module for 'XCTest'
import XCTest
public extension XCTestCase {
func makeExpectation() -> XCTestExpectation {
return expectation(withDescription: "")
}
}
I've created an xcworkspace here (https://github.com/dtweston/TestFrameworkSample) that demonstrates a solution to your issue. There are two projects in this workspace:
SampleApp project with an iOS app target and a unit test target.
SharedTestFramework project that imports XCTest and declares the single extension you put in your question.
The SampleAppTests target links to the SharedTestFramework to be able to use the extension it defines. The single test file imports the SharedTestFramework.
With those steps, I also encounter the Cannot load underlying module for 'XCTest' when building the SharedTestFramework.
The fix for that is to update the Framework Search Paths to include "$(PLATFORM_DIR)/Developer/Library/Frameworks". Now the SharedTestFramework compiles correctly, and as you can see in the workspace I uploaded, the SampleAppTests target is able to use it successfully.
Old and busted answer
Are you building a separate framework that is designed to be imported into test targets? If that's the case then I think you just need to reference XCTest.framework from this custom framework you're building.
On the other hand, if you're trying to add this extension to a framework that is used by your app target, that seems like a bad idea, because it would mean linking XCTest.framework to the binary that goes to the store and runs on people's devices.
I'm not sure if that's possible. I'm more confident that it's not a scenario Apple expects or supports.

How do I create an importable module in Swift?

I have read through Apple's documentation for Swift and can find nothing about how to create modules or how to define class or stucture members as private or public.
There are references to the import statement in the syntax but I can find no information on what it does or how to use it.
Does anyone know where I can find this?
In Swift, "Modules" refers to Frameworks. Xcode now has a template for creating a framework project for both iOS and OS X.
There is currently no way to declare methods or properties public / protected. If you would like to see this added as a feature, you can make a feature request on Apple's bug reporter. It should also be noted that Apple has stated that the language could change with each release of Xcode, so it is possible that member access levels could be added before the public release.
Also, there is a way to make a module by yourself, but it's a bit harder way.
If you'll look at xcrun swift -help you may see a few options, and there are -emit-module, -emit-library and -emit-object which might be useful, but, probably, you should prefer official way and distribute modules via Frameworks.
If you still want to make module on your own, you can read this guide with some explanation
Apple mentioned that private methods don't exist yet but they are in the process of being implemented. Remember that this is a newborn language and it is still being build up.
Update
You can modularize a swift project using frameworks.
We modularize by creating separate framework projects for each module and link them via Xcode workspace. It looks more natural when we separate the components into different projects & it also makes sure that there is only a one-way communication between modules. Developers can work/test on isolation without thinking much about other modules.
By default classes/structs/etc created within a framework will have an internal access control type so it is only visible within the modules. In order to make it visible outside the module, you can make it public.
More info on access controll level here
The latest Xcode 6 beta update (beta 4) bring access control to swift
Swift Enables Access Control
Swift access control has three access levels:
private entities can only be accessed from within the source file where they are defined.
internal entities can be accessed anywhere within the target where they are defined.!
public entities can be accessed from anywhere within the target and from any other context that imports the current target’s module.
Swift 4.0
Description from the chapter "Access Control" in the Apple book "The Swift Programming Language (Swift 4 Edition)"
Swift provides five different access levels for entities within your code. These access levels are relative to the source file in which an entity is defined, and also relative to the module that source file belongs to.
open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework. The difference between open and from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework.
internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
fileprivate access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.”

Loading a private frameworks in MacRuby

I'm attempting to follow this tutorial 'Demystifying Mail.app Plugins on Leopard' to build a Mail.app plugin. Instead of using PyObjC I'm trying to use MacRuby. I've got MacRuby 0.6 loaded up and I've gotten to this step in the tutorial (PyObjC code):
MVMailBundle = objc.lookUpClass('MVMailBundle')
I've search the web a bit but can't seem to find any information about loading the private framework 'MVMailBundle' in MacRuby. Any ideas?
Thanks in advance - AYAL
I think the idea is that this plugin will be loaded into Mail.app, which will already have loaded the private framework in question. So we just want to look up a class at runtime (which is what that Python snippet is doing — not loading a framework). The way to do this in MacRuby is MVMailBundle = NSClassFromString 'MVMailBundle'.
(You will need to include framework 'Cocoa' in order to get the NSClassFromString method, but I assume you'll have already done this.)
MacRuby uses garbage collection and Mail doesn't. You can't load a GC bundle into a non-GC app. So this is a dead-end.