I am working on a Swift framework which has majority of Swift files and very few Objective C files. Since I don't want my Objective-C files to be available outside the SDK and not even under headers folder, I gave them project access and now use them internally with a module map.
Here is how my modulemap file looks like:
module MySDKPrivate {
header "CardDetails.h"
export *
}
Now, in order to access this CardDetails file, I just need to import MySDKPrivate module in the required Swift class. All good till here.
Now, let's say I have a public class, named Payment.swift. This class is responsible for executing SDK operations partially hidden to the user, hence it is under the project access, so it does not show up in the headers when SDK is distributed.
There are some methods such as authenticate and getSessionExpireDate which are accessed by a PaymentDoor.swift class, which is basically nothing but an extension to Payment class for partial read & write injection, helping to get the minor info in/out of Payment class. Here is the sample signature of PaymentDoor.swift
//Extension to inject data in and out of the Internal Payment.swift
public extension Payment{
}
And here is the Payment.Swift class
import MySDKPrivate
public class Payment {
let username: String
let password: String
let cardDetails: CardDetails
public init(userName: String, password: String, cardNumber: String) {
self.username = userName
self.password = password
let cardDetails = CardDetails()
cardDetails.cardNumber = cardNumber
self.cardDetails = cardDetails
}
public func authenticate() -> Bool{
//Some logic
return true
}
private func generateAuthkey(){
//Should not be accessible with the extension
}
}
This is another reason why I want to keep this Payment class as public and inside project access for headers, because If I make it available as public under headers, SDK user would see the name of my modulemap and all the code in Payment class and could use the objective-c files as well with importing the module map. Although now also the name of the module map comes up by Auto Help but at least it's not visible directly.
I still think there could be a better way to inject data in and out of a public class which hides my module name OR Hiding Objective-C files could be done in a better way may be. If yes, please help !
Related
I'm working on a KMM app. The shared module has a helper class, that relies on different native libraries for the android part and for the iOS part. This is implemented with the already know "expected/actual" pattern.
As said, the iOS actual class makes use of an iOS framework, that performs some calculations, and returns an array of objects. The ios framework that creates the list of objects works correctly (it was tested with unit tests). A simplified example follows below.
This is the class of the objects that are inside of the array:
public class ChildNodeIos: NSObject{
public let content:String
public let isTextNode:Bool
public init(content:String,isTextNode:Bool=false){
self.content=content
self.isTextNode=isTextNode
}
}
The helper class on the iOS side that returns the list of objects would be something like that:
#objc public class IOSCoolHelper: NSObject {
#objc public func getChildNodes(message: String) -> [ChildNodeIos] {
//build the array of child nodes here and return them
}
}
In the kotlin shared module, inside the iOS expected class, the function is called like the following:
#Serializable
data class ChildNodeKN(val content :String,val isTextNode :Boolean=false)
import com.mydomain.iosframeworks.IosCoolHelper
actual class CoolHelper actual constructor(private val someStuff: String) : ICoolHelper {
actual override fun getChildNodes(message: String): List<ChildNodeKN> {
val iosHelper= IOSCoolHelper()
val swiftarray:List<ChildNodeIos> = iosHelper.getChildNodes(message)
//I was expecting to do something like that but it does not work (the content of "array is always empty"):
val kotlinList:List<ChildNodeKN> = swiftarray as List<ChildNodeIos>
return kotlinList
}
}
}
Or maybe if the list of swift objects can not be direct be casted to the equivalent kotlin object list, I was expecting to be able to iterate over the swift list and convert it to the kotlin list, something like that:
val kotlinList=mutableListOf<ChildNodeKN>()
swiftArray.foreach{
kotlinList.add(ChildNodeKN(it.content,it.isTextNode))
}
But again, the content of the swift Array is empty. Doing a lot of tests (I can no reproduce them now), I managed to access something inside the array, but it was not an object of type ChildNodeIos, nor something I could read on the kotlin side.
Well, the question is, how to receive on the kotlin side, a list with more or less complex objects inside, that was generated on the iOS side?
I have to say, that this swift helper class has many other functions that return primitive values (strings, booleans, or int), and that is working very well.
I suppose a workaround would be instead an array with objects, to return an array with primitive types and two dimensions from the Swift side, but I would like to work with an array of objects if it is possible.
Thank you for your help
I managed to find the solution by myself. The problem was the declaration of the Swift class of the object contained in the list. I forgot the #objc declaration for the properties of the class, because if that I was not able to read the objects inside the returned array.
public class ChildNodeIos: NSObject{
#objc public let content:String
#objc public let isTextNode:Bool
public init(content:String,isTextNode:Bool=false){
self.content=content
self.isTextNode=isTextNode
}
}
And then, on the Kotlin side, I did not achieve to cast it directly to a list, but with a foreach loop it is very easy to write the iOS objects in Kotlin objects:
Should I get the following error:
class.dart:11:11: Error: The getter '_privateID' isn't defined for the class 'Y'.
- 'Y' is from 'class.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named '_privateID'.
From the following code?
mixin.dart:
class Mixin {
static int _nextID = 0;
int publicID = _nextID++; // I only need one of these lines
int _privateID = _nextID++; // but this variable is inaccessible
}
class.dart:
import 'mixin.dart';
class X with Mixin {
void run() {
print(publicID); // no error here
}
}
class Y with Mixin {
void run() {
print(_privateID); // Error: _privateID not defined
}
}
void main() {
Y().run();
}
Or is this a bug? If it's not a bug, I'd like to understand why this behavior is reasonable.
When I instead define the mixin in the same file as the above classes, I get no error.
(Dart SDK 2.4.1.)
It is not a bug.
The private field is inherited, but you cannot access it because its name is private to a different library.
Dart's notion of "privacy" is library private names.
The name _privateID in the mixin.dart library introduces a library private name. This name is special in that it can only be written inside the same library.
If someone writes _privateID in a different library, it is a different name, one unique to that library instead.
It is as if private names includes the library URI of the library it is written in, so what you really declare is a name _privateID#mixin.dart.
When you try to read that field in class.dart, you write ._privateID, but because it is in a different library, what you really write is ._privateID#class.dart, a completely different name, and the classs does not have any declarations with that name.
So, if one class needs to access a private member of another class (or mixin, or anything), then the two needs to be declared in the same library, because otherwise they cannot even write the name of that variable.
That is why the code works if you write the mixin in the same library.
If you want to move the mixin to a separate file, but not necessarily a separate library, you can use a part file.
Swift 3 introduced the new open keyword that I'm using in a framework.
Does an open class in this framework require an open initialiser to be used outside of said framework, or does the init function inherit the open declaration on the class?
For example:
open class OpenClass {
var A: String
init() { // does this init() function need to be marked open?
A = String()
}
}
Side question: do the variables in the open class OpenClass inherit the open nature of their class?
From SE-0117 Allow distinguishing between public access and public overridability:
Initializers do not participate in open checking; they cannot be declared open, and there are no restrictions on providing an initializer that has the same signature as an initializer in the superclass.
You need not and you cannot declare a init method as open:
open class OpenClass {
open init() { // error: only classes and overridable class members can be declared 'open'; use 'public'
}
}
The default access level for all members of a class (properties
and methods) is internal, that applies to open classes as well.
So in Apple documentation:
Any type members added in an extension have the same default access level as type members declared in the original type being extended. If you extend a public or internal type, any new type members you add will have a default access level of internal.
Giving a sub class of UIView extension:
extension UIViewSubClass
{
var helloWorld : String {
get {
return "helloWorld"
}
}
}
This will mark helloWorld as internal, I have no problem, and I cannot see it in my Objective-C based project.
However, if I mark the extension to be public:
public extension UIViewSubClass
{
var helloWorld : String {
get {
return "helloWorld"
}
}
}
Now helloWorld apprears in my Objective-C based code, which means it is marked as public.
However, I don't see Apple mentions this, did it?
I just saw the documentation said a public class will still has implicit internal level.
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
private func somePrivateMethod() {} // explicitly private class member
}
Marking public for extension seems have different effect than marking a Class definition. This makes me confused.
Could someone help me, is this supposed to be so, or this is a sort of swift bug? I am with swift 2.1 and Xcode 7.2
Answer: Yes, placing access level modifiers (in your case, public, specifically) in front of extensions modify the default access level of all new types in the scope of that extension.
Note that the modifier does not affect the access level of the class/struct/etc being extended (only it's members. However, there are things that ought to be considered, as I will discuss below.
In this discussion, I'll post a few important facts regarding regarding access levels in Swift. All these are from the Swift Language Guide - Access Control - Access Levels.
Let's first assure what you've already stated:
All entities in your code (with a few specific exceptions, as
described later in this chapter) have a default access level of
internal if you do not specify an explicit access level yourself.
OK, this goes in line with what you've quoted in you question: any new type members, whether in a class or structure definition, will have a default access level of internal.
Now, lets look at the access level modifier that you can add in front of extensions:
You can extend a class, structure, or enumeration in any access
context in which the class, structure, or enumeration is available.
Any type members added in an extension have the same default access
level as type members declared in the original type being extended. If
you extend a public or internal type, any new type members you add
will have a default access level of internal. If you extend a private
type, any new type members you add will have a default access level of
private.
Alternatively, you can mark an extension with an explicit access level
modifier (for example, private extension) to set a new default access
level for all members defined within the extension. This new default
can still be overridden within the extension for individual type
members.
This sorts things out. We look at your example, and assume that your class UIViewSubClass has access level public (or compile time error, se below):
/* no access level modifier: default access level will be 'internal' */
extension UIViewSubClass
{
// default access level used: internal
var helloWorld : String {
get {
return "helloWorld"
}
}
}
// modify default access level to public
public extension UIViewSubClass
{
// default access level used: public
var helloWorld : String {
get {
return "helloWorld"
}
}
}
With the discussion above in mind, it's expected that your helloWorld in you public extension ... is marked as internal, as this is, in this context, the default access level. In the context of extensions, access level modifiers work differently than when applied to types.
Finally, we should point out that using a public access modifier when extending a non-public class will yield a compile time error in Swift. So in your case above:
If UISubViewClass is an internal or a private class, then the public extension ... on the class will yield an compile time error.
If UISubViewClass is a public class, then adding the public extension will be redundant as the default access modifier of a public class is already, by definition, public.
I'd say that the error described above is not really an error to avoid runtime errors, but rather to avoid redundant (and confusing) code: public member types or private or internal classes will never make use of itspublic` access level.
class MyImplicitlyInternalClass {
private var myExplicitlyPrivateVar = 0
var myImplicitlyInternalVar = 0
public var myExplicitlyPublicVar = 0 // warning, see (Note 1) below
// redundant 'public': can never be accessed publicly
// myExplicitlyPublicVar will behave as 'internal'
}
public extension MyImplicitlyInternalClass { // error, see (Note 2)
var myNewVarIsInternal : Int { get { return 0 } }
}
/* (Note 1) Compile type warning:
"Declaring a public var for an internal class."
(Note 2) Compile time error:
"Extension of internal class cannot be declared public."
Summary: in theory, these two above are the same type of
'faults', but only the latter is flagged as and error. */
Hence, it only ever makes sense to use the access level modifiers on extensions to make the default access level more restrictive, i.e., using internal extension ... of a public class, or private extension or an internal class.
The documentation only mentions nested types, but it's not clear if they can be used as namespaces. I haven't found any explicit mentioning of namespaces.
I would describe Swift's namespacing as aspirational; it's been given a lot of advertising that doesn't correspond to any meaningful reality on the ground.
For example, the WWDC videos state that if a framework you're importing has a class MyClass and your code has a class MyClass, those names do not conflict because "name mangling" gives them different internal names. In reality, however, they do conflict, in the sense that your own code's MyClass wins, and you can't specify "No no, I mean the MyClass in the framework" — saying TheFramework.MyClass doesn't work (the compiler knows what you mean, but it says it can't find such a class in the framework).
My experience is that Swift therefore is not namespaced in the slightest. In turning one of my apps from Objective-C to Swift, I created an embedded framework because it was so easy and cool to do. Importing the framework, however, imports all the Swift stuff in the framework - so presto, once again there is just one namespace and it's global. And there are no Swift headers so you can't hide any names.
EDIT: In seed 3, this feature is now starting to come online, in the following sense: if your main code contains MyClass and your framework MyFramework contains MyClass, the former overshadows the latter by default, but you can reach the one in the framework by using the syntax MyFramework.MyClass. Thus we do in fact have the rudiments of a distinct namespace!
EDIT 2: In seed 4, we now have access controls! Plus, in one of my apps I have an embedded framework and sure enough, everything was hidden by default and I had to expose all the bits of the public API explicitly. This is a big improvement.
Answered by SevenTenEleven in the Apple dev forum:
Namespaces are not per-file; they're per-target (based on the
"Product Module Name" build setting). So you'd end up with something
like this:
import FrameworkA
import FrameworkB
FrameworkA.foo()
All Swift declarations are considered to be part of
some module, so even when you say "NSLog" (yes, it still exists)
you're getting what Swift thinks of as "Foundation.NSLog".
Also Chris Lattner tweeted about namespacing.
Namespacing is implicit in Swift, all classes (etc) are implicitly
scoped by the module (Xcode target) they are in. no class prefixes
needed
Seems to be very different what I have been thinking.
While doing some experimentation with this I ended up creating these "namespaced" classes in their own files by extending the root "package". Not sure if this is against best practices or if it has any implications I'm mot aware of(?)
AppDelegate.swift
var n1 = PackageOne.Class(name: "Package 1 class")
var n2 = PackageTwo.Class(name: "Package 2 class")
println("Name 1: \(n1.name)")
println("Name 2: \(n2.name)")
PackageOne.swift
import Foundation
struct PackageOne {
}
PackageTwo.swift
import Foundation
struct PackageTwo {
}
PackageOneClass.swift
extension PackageOne {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
PackageTwoClass.swift
extension PackageTwo {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Edit:
Just found out that creating "subpackages" in above code wont work if using separate files. Maybe someone can hint on why that would be the case?
Adding following files to the above:
PackageOneSubPackage.swift
import Foundation
extension PackageOne {
struct SubPackage {
}
}
PackageOneSubPackageClass.swift
extension PackageOne.SubPackage {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Its throwing a compiler error:
'SubPackage' is not a member type of 'PackageOne'
If I move the code from PackageOneSubPackageClass.swift to PackageOneSubPackage.swift it works. Anyone?
Edit 2:
Fiddling around with this still and found out (in Xcode 6.1 beta 2) that by defining the packages in one file they can be extended in separate files:
public struct Package {
public struct SubPackage {
public struct SubPackageOne {
}
public struct SubPackageTwo {
}
}
}
Here are my files in a gist:
https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8
I believe this is achieved using:
struct Foo
{
class Bar
{
}
}
Then it can be accessed using:
var dds = Foo.Bar();
Namespaces are useful when you need to define class with the same name as class in existing framework.
Suppose your app has MyApp name, and you need to declare your custom UICollectionViewController.
You don't need to prefix and subclass like this:
class MAUICollectionViewController: UICollectionViewController {}
Do it like this:
class UICollectionViewController {} //no error "invalid redeclaration o..."
Why?. Because what you've declared is declared in current module, which is your current target. And UICollectionViewController from UIKit is declared in UIKit module.
How to use it within current module?
var customController = UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
How to distinguish them from another module?
var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Swift uses modules much like in python (see here and here) and as #Kevin Sylvestre suggested you can also use the nested types as namespaces.
And to extend the answer from #Daniel A. White, in WWDC they were talking about the modules in swift.
Also here is explained:
Inferred types make code cleaner and less prone to mistakes, while
modules eliminate headers and provide namespaces.
You can use extension to use the mentioned structs approach for namespacing without having to indent all of your code towards the right. I've been toying with this a bit and I'm not sure I'd go as far as creating Controllers and Views namespaces like in the example below, but it does illustrate how far it can go:
Profiles.swift:
// Define the namespaces
struct Profiles {
struct Views {}
struct ViewControllers {}
}
Profiles/ViewControllers/Edit.swift
// Define your new class within its namespace
extension Profiles.ViewControllers {
class Edit: UIViewController {}
}
// Extend your new class to avoid the extra whitespace on the left
extension Profiles.ViewControllers.Edit {
override func viewDidLoad() {
// Do some stuff
}
}
Profiles/Views/Edit.swift
extension Profiles.Views {
class Edit: UIView {}
}
extension Profiles.Views.Edit {
override func drawRect(rect: CGRect) {
// Do some stuff
}
}
I haven't used this in an app since I haven't needed this level of separation yet but I think it's an interesting idea. This removes the need for even class suffixes such as the ubiquitous *ViewController suffix which is annoyingly long.
However, it doesn't shorten anything when it's referenced such as in method parameters like this:
class MyClass {
func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
// secret sauce
}
}
Even though it is possible to implement namespaces using Framework and Libraries but the best solution is to use local packages using Swift Package Manager. Besides having access modifiers, this approach has some other benefits. As in Swift Package Manager, the files are managed based on the directory system, not their target member ship, you won't have to struggle with merge conflicts that arise frequently in teamworks. Furthermore, there is no need to set file memberships.
To check how to use local Swift packages refer to the following link:
Organizing Your Code with Local Packages
In case anyone was curious, as of June 10th 2014, this is a known bug in Swift:
From SevenTenEleven
"Known bug, sorry! rdar://problem/17127940 Qualifying Swift types by their module name doesn't work."