Dividing a Swift application's components into Swift modules - xcode

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.

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

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.

Related

Why Are My Dependent Frameworks Being Pulled In Twice?

[Note: This question is, I think, dealing with the consequences of what prompted this never answered question]
I have a workspace that includes both a framework target and an application target. When I run the application many messages of the following form come to the console: Class "C" is implemented in both "Binary1" and "Binary2". One of the two will be used. Which one is undefined.
Here is a sampling of those messages:
objc[65093]: Class FIRAIdentifiers is implemented in both /Users/Robert/Library/Developer/Xcode/DerivedData/GenerationOfNow-bmsridmvnbtgfiduzqqomicqvsns/Build/Products/Debug-iphonesimulator/VerticonsToolbox.framework/VerticonsToolbox (0x10804bd20) and /Users/Robert/Library/Developer/CoreSimulator/Devices/33628599-9570-4784-B324-DAC383435F75/data/Containers/Bundle/Application/26411B80-FB71-44E9-AA64-05FEF20B9F08/GenerationOfNow.app/GenerationOfNow (0x10674a150). One of the two will be used. Which one is undefined.
objc[65093]: Class FIRASearchAdReporter is implemented in both /Users/Robert/Library/Developer/Xcode/DerivedData/GenerationOfNow-bmsridmvnbtgfiduzqqomicqvsns/Build/Products/Debug-iphonesimulator/VerticonsToolbox.framework/VerticonsToolbox (0x10804bd70) and /Users/Robert/Library/Developer/CoreSimulator/Devices/33628599-9570-4784-B324-DAC383435F75/data/Containers/Bundle/Application/26411B80-FB71-44E9-AA64-05FEF20B9F08/GenerationOfNow.app/GenerationOfNow (0x10674a1a0). One of the two will be used. Which one is undefined.
objc[65093]: Class FIRAZeroingWeakContainer is implemented in both /Users/Robert/Library/Developer/Xcode/DerivedData/GenerationOfNow-bmsridmvnbtgfiduzqqomicqvsns/Build/Products/Debug-iphonesimulator/VerticonsToolbox.framework/VerticonsToolbox (0x10804bde8) and /Users/Robert/Library/Developer/CoreSimulator/Devices/33628599-9570-4784-B324-DAC383435F75/data/Containers/Bundle/Application/26411B80-FB71-44E9-AA64-05FEF20B9F08/GenerationOfNow.app/GenerationOfNow (0x10674a218). One of the two will be used. Which one is undefined.
All of the duplicated class definitions come from Firebase frameworks which were installed into my workspace via the Pod file demonstrated in this question. Here is a screen shot of my workspace's Navigator:
Notice that both the framework target (VerticonsToolbox) and the application target (GenerationOfNow) are referencing the pods.
It seems to me that the proper way for a framework to be built is that it should not embed whatever frameworks it is linked against. It should be the responsibility of whatever application uses that framework to pull in the other dependencies. And indeed, when I examine the build phases for the framework VerticonsToolbox there is no option for specifying embedded binaries whereas there is for the application GenerationOfNow.
So, I am at a lose as to how to proceed. I suspect that what is happening is a result of the things that are put in place when pod install is executed. Can anyone advise me?
BTW: Can anyone point me to a good write up on how Xcode builds, what the various settings are, what tools there are for examining the binaries, etc? With Xcode everything is fine until it isn't and then there is this big, mysterious soup of stuff. Jeez!

Swift impact of performance when splitting project up into frameworks

Are there any drawbacks of splitting up a Swift project into several frameworks? In my current application I don't need to do this but it would logically separate the different parts of my code so I intended to put the code into multiple frameworks (embedded binaries). However, I don't know if Swift's apparently awesome feature 'Whole Module Optimization' is affected by creating several modules.
Furthermore, are there any other drawbacks of using frameworks or performance upsides?
Thanks for your clarifications ;)
One thing I know is that with framework, you can reduce compiling time. Because you can compile your stable framework and reuse it. Then xcode only compile your main project.
The drawback (in my opinion) is that you have to import framework whenever you use it.

How do you create a basic TemplateInfo.plist file with Ancestors and specific frameworks?

Whenever I create a new project I add my own class files -often the same ones. It has become tedious to manually create these over and over. I want an automatic way to do this. I discovered custom Project Templates and I'm not sure if they are well documented by Apple.
I can create a cocos2D project with my custom classes and it works wonders. The problem is when I create a basic empty application with my custom classes.
Basically okay I'll give a little context. See my TemplateInfo.plist: This works. Note the "Ancestors" part. Here's how I interpret it: this inherits from cocos2d templates and does things like:
add necessary frameworks
adds cocos2D things like the splash image
other project-specific things
Now I don't know where these ancestor templates are or what format they are in, so I can't yet break down the individual things and say "OK I want the frameworks they include but not the launch image," but that's essentially what I want to do. I want to start with the most basic template and build from there manually.
I discovered that I can add specific frameworks in the "Targets" area. But before tackling that, I have a question: What Ancestors do I use, so that it does not use the Cocos2D base templates? I want the most basic thing.
1) Try removing all Ancestors and create a new Project and Xcode won't let you name the project and it crashes.
2) Try including only the Ancestors for the Default Empty Application: com.apple.dt.unit.cocoaTouchApplication
And it adds the correct frameworks:
Foundation
CoreGraphics
UIKit
XCTest
which is cool. However I noticed it overrides my Prefix.pch with a [Project-Name]-Prefix.pch and my project does not compile because I include a Debug Logger statement in my custom Prefix.ch like this:
#ifdef DEBUG
#define DLog(...) NSLog(__VA_ARGS__)
I figure this is the "com.apple.dt.unit.cocoaTouchApplication" ancestor doing its thing, and I want custom behavior that's different.
Is there a list of Ancestors somewhere that I can look at? Or is there a way I can look at the specific settings defined in "com.apple.dt.unit.cocoaTouchApplication" (if that even makes sense)?
Has anyone else run into this? What techniques do you use? Does anyone wish for an easier way to do this? Or is it better to create files manually every time?
I'm spinning wheels and any insight would be helpful. Thanks.
I figured it out. Searching through Xcode's default templates, I found one that uses basic ancestors. I used the following two:
And in Targets you can easily add the frameworks you want to include:
There are many little quirks like how to include an Info.plist file and Prefix.pch file and how to get it to rename properly (and replace the default one placed by the template). I am creating a video tutorial to show all these things in detail.
Creating your own custom templates is quite satisfying. Saves a lot of time and hassle when beginning new projects.

How to structure a Cocoa project

When I write in Python I can structure my source code as several files and folders, effectively creating the modules. I then can import a module, a file can have several classes, and so on.
How to structure a project written in Cocoa? Can a .m file implement multiple classes? Is there a notion of a "module"? What exactly is a "framework" for? Is it possible to create a framework of mine? How do I "import" it into another project? And what happens at compilation time, does the compiler embed frameworks into one giant executable or are they living next to the executable, merely being copied into the bundle folder?
It really sounds like you should start with some Apple resources like Your First Mac App and What are Frameworks?
To answer your questions specifically - a .m can contain multiple class implementations; a Framework is a bundle of shared resources; a Framework is a library for reuse; you can create your own Frameworks; in a typical Mac application bundle the frameworks are copied into the application bundle.
There are also a bunch of good resources concerning the structure and design of Cocoa applications - I particularly like this Cocoa With Love post.

Add a 3rd party framework to OS X Xcode 4 project

I want to add Oomph's MacMapKit as a framework to my Xcode (v4.3.2) project. I've already added a couple of standard Apple frameworks to my project (which was a breeze) but this is new to me and offers a rather intimidating array of settings and options.
Before I start clicking around in my project/build settings, I would like to ask:
(1) Is the MacMapKit framework in its current state technically speaking
"a framework"? From this post I gather that a Cocoa framework
requires some special preparation with Xcode and as such MacMapKit
would possibly not qualify.
(2) What would be the best way to add MacMapKit (source and resource files) in its current state to
my own project? I've read about various approaches which all seem to
assume you have an actual framework, which I'm not sure MacMapKit
qualifies for (see question 1).
This MacMapKit has several targets. One of the targets is the MapKit Framework.
You need to build this target first in order to use it like a common framework.
So just select the "MapKitFramework" target from the schemes list and build.

Resources