14

I'm writing an iOS application in Swift, and I'm trying to figure out how to organize the project into separate modules. I'm using an MVVM architecture, and I want to make the Model, ViewModel, and View components separate Swift modules that make only subsets of themselves accessible to the modules that import them. The files in the View would import the ViewModel, and files in the ViewModel would import the Model. How can I accomplish this? Note that I'm not trying to create libraries that multiple applications can share. I'm just trying to enforce separation of components using modules.

EDIT: Maybe the question is, "What mechanism should I use to create modules aside from the one that comes with the initial iOS application project?"

One of the answers in "How do you use Namespaces in Swift?" https://stackoverflow.com/a/24032860/215400 says, "classes (etc) are implicitly scoped by the module (Xcode target) they are in." From that, one might conclude that targets correspond to modules and that the answer is to create separate targets within an Xcode project, but I tried that earlier, and tskulbru is saying that I need multiple Xcode projects.

Regarding multiple Xcode projects, the File > New > Project > iOS Framework & Library > Cocoa Touch Framework option didn't look right because it's supposed to be for things that use UIKit, and two of the modules I want to create shouldn't depend on UIKit. The other "Framework & Library" option, Cocoa Touch static library, isn't an option with Swift.

Another StackOverflow post mentioned using private Pods. After spending an hour working on that, I concluded that it wasn't the right solution because I shouldn't have to edit these modules in different workspaces.

Community
  • 1
  • 1
  • Possible duplicate of [How do you use Namespaces in Swift?](http://stackoverflow.com/questions/24002821/how-do-you-use-namespaces-in-swift) – tskulbru Jan 01 '16 at 18:59
  • Why not use the `private` qualifier to enforce encapsulation? – Marc Khadpe Jan 01 '16 at 19:44
  • 1
    @Marc--Thanks, but making things private to the files they're defined in isn't what I want. The public vs. internal access that Modules provide (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html) is exactly what I want. – Christopher Simmons Jan 01 '16 at 21:16
  • 1
    @ChristopherSimmons The name “Cocoa Touch Framework” is misleading, and probably chosen by Apple for marketability reasons.  Really, it's should be called a “Darwin Framework” or similar.  What a “Cocoa Touch Framework” really builds is a dynamic shared library packaged up in a `….framework`— it can depend on UIKit or AppKit or Foundation or CoreFoundation or any other framework(s) or even have no dependencies (depending only on std/system C APIs).  To make it not depend on UIKit, go into the project setting and replace `UIKit.framework` in Linked Frameworks with `Foundation.framework`. – Slipp D. Thompson Feb 13 '17 at 05:06

2 Answers2

6

This isn't possible without creating separate projects for the modules you want to create. This is because the way Swift handles namespacing.

Eonil answered this better than me: https://stackoverflow.com/a/24032860/215400 (Copy below)

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

fphilipe
  • 9,739
  • 1
  • 40
  • 52
tskulbru
  • 2,336
  • 1
  • 20
  • 38
  • 1
    If you copy major parts from another answer then please add a link to the original for proper attribution: http://stackoverflow.com/a/24032860/1187415. – Martin R Jan 01 '16 at 19:17
  • Heh, I actually think your answer is closer to what I'm looking for, tskulbru. If I need to create separate projects, how should I go about that? Should I go with the "Cocoa Touch Framework" option despite the fact that I don't need UIKit? It seems like "Cocoa Touch Static Library" would make more sense, but it's not an option with Swift. As mentioned above, I started going down the path of a private Pod, but that didn't look too good. – Christopher Simmons Jan 01 '16 at 21:24
  • You need to create a dynamic framework if you want to use modules, if you create a static libary you would need to include it in your bridging-header file which kind of ruins the point of the whole thing. See here for a tutorial on how to create a dynamic framework https://medium.com/@PyBaig/build-your-own-cocoa-touch-frameworks-in-swift-d4ea3d1f9ca3 – tskulbru Jan 02 '16 at 11:42
1

From my perspective if you want to encapsulate your components, probably you have two solutions:

  • Framework
  • Internal cocoapods

Both solutions will give you fully encapsulated modules, where you can define API that will be available in project through public keyword. All other things will be not visible in your core project.

Managing your project will cost you a lot more time, but if you write this using SOLID principles, probably you will get more reusable code and those frameworks could be imported to other project just using import definition.

user3292998
  • 209
  • 1
  • 8