How to have a good project code organization in Xcode? - swift

My project i becoming messy because of the number of files, i need advices from devs.
For exemple i have string extensions, at start i was creating one per target. One for ios, one for watch OS etc ...
so I had something like :
extensionString.swift for iOS
extensionStrinWatchg.swift for watchOS
extensionStringTE.swift for today Extension
But with that method i got common code part between some targets and repeating code is never good, you agree.
So i started to make commun String extension files like
extensionStringIOS+watch.swift
extensionStringIOS+TD+watch.swift
extensionStringIOS+TD.swift
etc ...
adding to this the version that are only for a specific target we get :
extensionString.swift for ios
extensionStrinWatch.swift for watchOS
extensionStringTE.swift for today Extension
extensionStringIOS+watch.swift
extensionStringIOS+TD+watch.swift
extensionStringIOS+TD.swift
etc ...
You understand ... Thats a lot and that's messy. I loose time to find in which file to go
So now imagine this method for everything ...
The number of file become huge ...
I have that problem for extension, classes and others ...
So one file per target -> repeating code
second method : huge number of files, messy project and time wasting
So i have an idea but i'd like to know if it's the right way to do. What's the convention ?
My idea is to have one single extensionString.swift shared between all targets in my project and use things like this for code parts that shouldn't be used for all targets
#if !os(watchOS)
// do something
#else
// do something different
#if
So this applied in my project (not only extensions but class etc ..) could make me delete tons of files and regroup like this function for exemple :
/// boolean indicating if the user enabled the weekend days setting
static var isWeekendDaysSettingEnabled: Bool {
#if !os(watchOS)
if IS_ICLOUD_ENABLED {
return NSUbiquitousKeyValueStore.default.bool(forKey: KeyValueID.weekendDays.rawValue)
} else {
return UserDefaults.standard.bool(forKey: KeyValueID.weekendDays.rawValue)
}
#else
return UserDefaults.standard.bool(forKey: "weekendDays")
#endif
}

I think every answer here should start with big fat IMHO
There's no silver bullet and you have to decide which way to choose depending on your particular project.
As for me, I would try to separate all the logic into small frameworks, eg ExtensionsCrossplatform, ExtensionsIOS, ExtensionsWatchOS, etc.
An idea of having everything in one place is not so good and will lead to tight coupling between all the modules and platforms.
In fact, many big apps are comprised of big amount of tiny modules (more than 50).
Here's a video, which can describe the idea more https://badootech.badoo.com/the-immense-benefits-of-not-thinking-in-screens-6c311e3344a0

Related

Build a macos executable from an ios app's source code

I have an iOS app with some business logic. I would like to create an executable which would expose some calculations from the cli, so that I don't have to use the app.
Unfortunately, the code with the business logic that I want to expose depends on lots of other parts of the code, eventually pulling almost all the code. Even worse, it depends on some dependencies that are not available on macos (UIKit or DJISDK via pods).
I just want to write a main.swift, parse the arguments and use some functions from the codebase. What is the simplest way to do that ?
I don't mind if it's ugly (like pulling whole of UIKit in the executable) as long as it doesn't take to much time to implement.
I have two ideas:
remove all dependencies, optionally defining "dummy" classes compiled only for this target for some dependencies. That requires changing a lot of code, but seems to be cleaner.
find a way to pull the whole app in the new target. For example I have a UnitTests target which does that and can run on mac (though an app window appears when the tests run). But I have no idea how to do that.
Any help or guidance to good documentation would be greatly appreciated :)
I did my best to remove all unneeded dependencies, but there was a lot left that couldn't be removed easily. I resulted in using "dummy" classes to be able to compile without providing any logic.
Though it's not super elegant, it's a nice way to cut the dependency chain, and will let me remove the dependencies over time, while still being able to compile.
Ex:
// In common source file
class Business {
func usefulComputation() -> String {
...
}
func unneededFunction() -> UselessObject {
...
}
// In my CLI-target project
// file `DummyUselessObject.swift`
class UselessObject {
// add any function called from source file here, without putting any logic
}
// `main.swift`
let business = Business()
print(business.usefulComputation())

Is it possible to distribute the code of a class to several files?

Is it possible to distribute the code of a class to several files?
Honestly, I think the best route is to break it up into different roles that you compose into your class.
After all, how are you planning on breaking up your class?
Are you going to group methods and attributes according to similarity?
At that point you've just about come up with a role, so you might as well make it a role.
If you look at the source for Rakudo you see things like this:
class Perl6::Metamodel::ClassHOW
does Perl6::Metamodel::Naming
does Perl6::Metamodel::Documenting
does Perl6::Metamodel::LanguageRevision
does Perl6::Metamodel::Stashing
does Perl6::Metamodel::AttributeContainer
does Perl6::Metamodel::MethodContainer
does Perl6::Metamodel::PrivateMethodContainer
does Perl6::Metamodel::MultiMethodContainer
does Perl6::Metamodel::MetaMethodContainer
does Perl6::Metamodel::RoleContainer
does Perl6::Metamodel::MultipleInheritance
does Perl6::Metamodel::DefaultParent
does Perl6::Metamodel::C3MRO
does Perl6::Metamodel::MROBasedMethodDispatch
does Perl6::Metamodel::MROBasedTypeChecking
does Perl6::Metamodel::Trusting
does Perl6::Metamodel::BUILDPLAN
does Perl6::Metamodel::Mixins
does Perl6::Metamodel::ArrayType
does Perl6::Metamodel::BoolificationProtocol
does Perl6::Metamodel::REPRComposeProtocol
does Perl6::Metamodel::InvocationProtocol
does Perl6::Metamodel::ContainerSpecProtocol
does Perl6::Metamodel::Finalization
does Perl6::Metamodel::Concretization
does Perl6::Metamodel::ConcretizationCache
{
… # only 300 lines of code
}
If you do a good job of splitting up your roles, you should be able to use them in many classes.
How many classes in the Rakudo code base do you think compose in the Perl6::Metamodel::Naming role?
That role only provides a few things, and is only 45 lines long.
Here is an abbreviated version.
(All of the code in the methods has been deleted here for brevity.)
role Perl6::Metamodel::Naming {
has $!name;
has $!shortname;
method name($obj) {
…
}
method set_name($obj, $name) {
…
}
method shortname($obj) {
…
}
method set_shortname($obj, $shortname) {
…
}
}
Yes, there always is. But there us no standard supported way (yet anyway).
You can take the approach that Raku takes itself when creating the core settings: concatenate the files into a single file, and compile that. When you're building Rakudo from scratch, that's what's happening when you see the line:
+++ Generating gen/moar/foo
The generated files can be inspected in the gen/moar directory. At one point, I brought this up in a problem solving issue, but that never went anywhere. Perhaps that should be revisited.
You can use augment class. But that currently only makes sense inside a single file, because with multiple files it is creating multiple versions of the same "module" in pre-compilation. And the system is not able to determine what to resolve to what. This is when you realize that pre-compilation of Raku modules currently is creating libraries that need to be statically linkable at runtime.

Pre-processor macros in xcconfig files

is is possible to use macros in config files? I want to achieve something like:
if iPad
set variable to 1
else
set variable to 0
Is that possible? I would rather not use scripts for this.
You generally should check this at runtime rather than compile time. See iOS - conditional compilation (xcode).
If you don't do it that way, I typically recommend using different targets as hinted at by #Robert Vojta.
That said, I can imagine cases where this would be useful in some piece of shared code. So...
There is an xcconfig variable you can use called TARGETED_DEVICE_FAMILY. It returns 1 for iPhone and iPod Touch, and 2 for iPad. This can be used to create a kind of macro. I don't highly recommend this approach, but here's how you do it. Let's say you were trying to set some value called SETTINGS:
// Family 1 is iPhone/iPod Touch. Family 2 is iPad
SettingsForFamily1 = ...
SettingsForFamily2 = ...
SETTINGS = $(SettingsForFamily$(TARGETED_DEVICE_FAMILY))
I've done this a few times in my projects (for other problems, not for iPad detection). Every time I've done it, a little more thought has allowed me to remove it and do it a simpler way (usually finding another way to structure my project to remove the need). But this is a technique for creating conditionals in xcconfig.
AFAIK it's not possible. But if you want to solve simple task - lot of common settings and just few variables have different values, you can do this:
generic.xcconfig:
settings for both configs
ipad.xcconfig:
#include "generic.xcconfig"
ipad-specific-settings
iphone.xcconfig
#include "generic.xcconfig"
iphone-specific-settings
This can solve your condition need. I do use this schema frequently.
That's not possible. Configuration files are not preprocessed (and compiled). What are you trying to do?

Using table-of-contents in code?

Do you use table-of-contents for listing all the functions (and maybe variables) of a class in the beginning of big source code file? I know that alternative to that kind of listing would be to split up big files into smaller classes/files, so that their class declaration would be self-explanatory enough.. but some complex tasks require a lot of code. I'm not sure is it really worth it spending your time subdividing implementation into multiple of files? Or is it ok to create an index-listing additionally to the class/interface declaration?
EDIT:
To better illustrate how I use table-of-contents this is an example from my hobby project. It's actually not listing functions, but code blocks inside a function.. but you can probably get the idea anyway..
/*
CONTENTS
Order_mouse_from_to_points
Lines_intersecting_with_upper_point
Lines_intersecting_with_both_points
Lines_not_intersecting
Lines_intersecting_bottom_points
Update_intersection_range_indices
Rough_method
Normal_method
First_selected_item
Last_selected_item
Other_selected_item
*/
void SelectionManager::FindSelection()
{
// Order_mouse_from_to_points
...
// Lines_intersecting_with_upper_point
...
// Lines_intersecting_with_both_points
...
// Lines_not_intersecting
...
// Lines_intersecting_bottom_points
...
// Update_intersection_range_indices
for(...)
{
// Rough_method
....
// Normal_method
if(...)
{
// First_selected_item
...
// Last_selected_item
...
// Other_selected_item
...
}
}
}
Notice that index-items don't have spaces. Because of this I can click on one them and press F4 to jump to the item-usage, and F2 to jump back (simple visual studio find-next/prevous-shortcuts).
EDIT:
Another alternative solution to this indexing is using collapsed c# regions. You can configure visual studio to show only region names and hide all the code. Of course keyboard support for that source code navigation is pretty cumbersome...
I know that alternative to that kind of listing would be to split up big files into smaller classes/files, so that their class declaration would be self-explanatory enough.
Correct.
but some complex tasks require a lot of code
Incorrect. While a "lot" of code be required, long runs of code (over 25 lines) are a really bad idea.
actually not listing functions, but code blocks inside a function
Worse. A function that needs a table of contents must be decomposed into smaller functions.
I'm not sure is it really worth it spending your time subdividing implementation into multiple of files?
It is absolutely mandatory that you split things into smaller files. The folks that maintain, adapt and reuse your code need all the help they can get.
is it ok to create an index-listing additionally to the class/interface declaration?
No.
If you have to resort to this kind of trick, it's too big.
Also, many languages have tools to generate API docs from the code. Java, Python, C, C++ have documentation tools. Even with Javadoc, epydoc or Doxygen you still have to design things so that they are broken into intellectually manageable pieces.
Make things simpler.
Use a tool to create an index.
If you create a big index you'll have to maintain it as you change your code. Most modern IDEs create list of class members anyway. it seems like a waste of time to create such index.
I would never ever do this sort of busy-work in my code. The most I would do manually is insert a few lines at the top of the file/class explaining what this module did and how it is intended to be used.
If a list of methods and their interfaces would be useful, I generate them automatically, through a tool such as Doxygen.
I've done things like this. Not whole tables of contents, but a similar principle -- just ad-hoc links between comments and the exact piece of code in question. Also to link pieces of code that make the same simplifying assumptions that I suspect may need fixing up later.
You can use Visual Studio's task list to get a listing of certain types of comment. The format of the comments can be configured in Tools|Options, Environment\Task List. This isn't something I ended up using myself but it looks like it might help with navigating the code if you use this system a lot.
If you can split your method like that, you should probably write more methods. After this is done, you can use an IDE to give you the static call stack from the initial method.
EDIT: You can use Eclipse's 'Show Call Hierarchy' feature while programming.

Xcode - exclude files in a custom configuration - better way?

I'm trying to come up with a way to make it easy to switch out our "mock" data services and our live ones. Basically, we'll have live servers with real web services, but for whatever reason, a developer may want to load data from static files (file urls).
I figured I would solve this problem by creating categories that override the methods that fetch the data, thus leaving original code untouched (it has no concept of the "mock" data). I don't want to litter my code with #ifdef.
I can put an #ifdef at the very beginning of each file that has categories in it, and I can set a custom flag in the configuration settings, but I'd rather just have a way to include or exclude the files depending on the configuration. Is that possible? How do you solve this problem?
See http://lists.apple.com/archives/xcode-users/2009/Jun/msg00153.html
The trick is to define EXCLUDED_SOURCE_FILE_NAMES in the configuration you want to exclude the files from, and set the value of that custom build setting to a list of the file names (or a pattern that matches those, and only those, file names).
I would recommend creating two targets one of which has the mock categories included and another one which does not.
When you want to test, just build the target containing the mock categories. Everything else can remain identical.
If you would like to add a file but do not wont to compile it. Go to (for all your targets) project>build phases>compile source and take out the file that you do not want to compile.