project: add SwiftGen code generation

This commit adds the SwiftGen build phase. This phase launches the
swiftgen.sh script that uses SwiftGen tool to produce Swift Code
that make Strings, Assets and Storyboards usage a lot safer

Change-Id: I78471f5603864608e25bbad36f0459103d6bdded
diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj
index e381f8a..81009ca 100644
--- a/Ring/Ring.xcodeproj/project.pbxproj
+++ b/Ring/Ring.xcodeproj/project.pbxproj
@@ -34,7 +34,7 @@
 		043866211D218B1100E06CE2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 043866201D218B1100E06CE2 /* AudioToolbox.framework */; };
 		043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043866351D22D06500E06CE2 /* AccountTableViewCell.swift */; };
 		043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */; };
-		04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04399A021D1C2D9D00E99CD9 /* Assets.xcassets */; };
+		04399A031D1C2D9D00E99CD9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04399A021D1C2D9D00E99CD9 /* Images.xcassets */; };
 		04399A111D1C2D9D00E99CD9 /* RingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A101D1C2D9D00E99CD9 /* RingTests.swift */; };
 		04399A1C1D1C2D9D00E99CD9 /* RingUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A1B1D1C2D9D00E99CD9 /* RingUITests.swift */; };
 		04399A2A1D1C2DE300E99CD9 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399A291D1C2DE300E99CD9 /* CoreMedia.framework */; };
@@ -94,8 +94,12 @@
 		04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; };
 		1ABE07BA1F0C16F100D36361 /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07B91F0C16F100D36361 /* ContactViewModel.swift */; };
 		1ABE07BC1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07BB1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard */; };
-		1ABE07C11F0C22FB00D36361 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07BD1F0C22FB00D36361 /* LaunchScreen.storyboard */; };
-		1ABE07C21F0C22FB00D36361 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07BF1F0C22FB00D36361 /* Main.storyboard */; };
+		1ABE07D21F0D8FE800D36361 /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07D01F0D8FE800D36361 /* Images.swift */; };
+		1ABE07D31F0D8FE800D36361 /* Storyboards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07D11F0D8FE800D36361 /* Storyboards.swift */; };
+		1ABE07DC1F0D915100D36361 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07DA1F0D915100D36361 /* Localizable.strings */; };
+		1ABE07DF1F0D91A800D36361 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07DD1F0D91A800D36361 /* LaunchScreen.storyboard */; };
+		1ABE07E01F0D91A800D36361 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABE07DE1F0D91A800D36361 /* Main.storyboard */; };
+		1ABE07E21F0D924700D36361 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABE07E11F0D924700D36361 /* Strings.swift */; };
 		5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */; };
 		5557FD4A1E81AE850043E394 /* AccountModelHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5557FD491E81AE850043E394 /* AccountModelHelperTests.swift */; };
 		557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 557086511E8ADB9D001A7CE4 /* SystemAdapter.mm */; };
@@ -128,8 +132,6 @@
 		56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */; };
 		56BBC9A61ED7151500CDAF8B /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A51ED7151500CDAF8B /* MessageModel.swift */; };
 		56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A71ED7152300CDAF8B /* SmartlistViewController.swift */; };
-		56BBC9AA1ED7153800CDAF8B /* Global.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9A91ED7153800CDAF8B /* Global.strings */; };
-		56BBC9AC1ED7154300CDAF8B /* Smartlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */; };
 		56BBC9B01ED7155700CDAF8B /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */; };
 		56BBC9B41ED7156500CDAF8B /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */; };
 		56BBC9B51ED7156500CDAF8B /* ConversationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9B31ED7156500CDAF8B /* ConversationCell.xib */; };
@@ -145,7 +147,6 @@
 		56BBC9D41EDC7A6D00CDAF8B /* libargon2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56BBC9D31EDC7A6D00CDAF8B /* libargon2.a */; };
 		56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */; };
 		56BBC9E01EDDC9E600CDAF8B /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */; };
-		56BBC9E11EDDCA5900CDAF8B /* Walkthrough.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */; };
 		56BBC9E31EDDCC8100CDAF8B /* ConversationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9E21EDDCC8100CDAF8B /* ConversationSection.swift */; };
 		56BBC9E71EDE1DDF00CDAF8B /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9E61EDE1DDF00CDAF8B /* Colors.swift */; };
 /* End PBXBuildFile section */
@@ -202,7 +203,7 @@
 		043866371D2304A700E06CE2 /* BoolStringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoolStringExtension.swift; sourceTree = "<group>"; };
 		043999F31D1C2D9D00E99CD9 /* Ring.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ring.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
-		04399A021D1C2D9D00E99CD9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		04399A021D1C2D9D00E99CD9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
 		04399A071D1C2D9D00E99CD9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		04399A0C1D1C2D9D00E99CD9 /* RingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		04399A101D1C2D9D00E99CD9 /* RingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingTests.swift; sourceTree = "<group>"; };
@@ -271,8 +272,12 @@
 		04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libyaml-cpp.a"; path = "../fat/lib/libyaml-cpp.a"; sourceTree = "<group>"; };
 		1ABE07B91F0C16F100D36361 /* ContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = "<group>"; };
 		1ABE07BB1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = WalkthroughStoryboard.storyboard; sourceTree = "<group>"; };
-		1ABE07BE1F0C22FB00D36361 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
-		1ABE07C01F0C22FB00D36361 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		1ABE07D01F0D8FE800D36361 /* Images.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = "<group>"; };
+		1ABE07D11F0D8FE800D36361 /* Storyboards.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storyboards.swift; sourceTree = "<group>"; };
+		1ABE07DB1F0D915100D36361 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+		1ABE07DD1F0D91A800D36361 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Resources/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		1ABE07DE1F0D91A800D36361 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Resources/Main.storyboard; sourceTree = "<group>"; };
+		1ABE07E11F0D924700D36361 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
 		5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountModelHelper.swift; sourceTree = "<group>"; };
 		5557FD491E81AE850043E394 /* AccountModelHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountModelHelperTests.swift; sourceTree = "<group>"; };
 		557086501E8ADB9D001A7CE4 /* SystemAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemAdapter.h; sourceTree = "<group>"; };
@@ -300,7 +305,6 @@
 		5669A7FD1EA904E4003C7B93 /* TextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextCell.xib; sourceTree = "<group>"; };
 		5669A8021EAA58E6003C7B93 /* LinkDeviceToAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkDeviceToAccountViewController.swift; sourceTree = "<group>"; };
 		568F56721EA7E38F00132D7D /* PKHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PKHUD.framework; path = Carthage/Build/iOS/PKHUD.framework; sourceTree = "<group>"; };
-		56AC64DA1E8012CA00EA1AA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Walkthrough.strings; sourceTree = "<group>"; };
 		56AC64DE1E804ECC00EA1AA9 /* SwitchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchCell.swift; sourceTree = "<group>"; };
 		56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldCell.swift; sourceTree = "<group>"; };
 		56AC64E21E805F0200EA1AA9 /* TextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; };
@@ -571,6 +575,7 @@
 		043999F51D1C2D9D00E99CD9 /* Ring */ = {
 			isa = PBXGroup;
 			children = (
+				1ABE07C61F0D86B300D36361 /* Resources */,
 				56BBC9BD1ED7165800CDAF8B /* Smartlist */,
 				56BBC9B61ED7158600CDAF8B /* Contacts */,
 				56BBC9AD1ED7154800CDAF8B /* Conversations */,
@@ -585,10 +590,9 @@
 				043866341D22D04E00E06CE2 /* UI */,
 				043866391D2307C000E06CE2 /* Extensions */,
 				5628B4191F0C358D008B1E11 /* Settings */,
-				1ABE07BD1F0C22FB00D36361 /* LaunchScreen.storyboard */,
-				1ABE07BF1F0C22FB00D36361 /* Main.storyboard */,
 				043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */,
-				04399A021D1C2D9D00E99CD9 /* Assets.xcassets */,
+				1ABE07DD1F0D91A800D36361 /* LaunchScreen.storyboard */,
+				1ABE07DE1F0D91A800D36361 /* Main.storyboard */,
 				04399A071D1C2D9D00E99CD9 /* Info.plist */,
 			);
 			path = Ring;
@@ -688,6 +692,25 @@
 			name = SYS_DEPS;
 			sourceTree = "<group>";
 		};
+		1ABE07C51F0D862D00D36361 /* Generated */ = {
+			isa = PBXGroup;
+			children = (
+				1ABE07E11F0D924700D36361 /* Strings.swift */,
+				1ABE07D01F0D8FE800D36361 /* Images.swift */,
+				1ABE07D11F0D8FE800D36361 /* Storyboards.swift */,
+			);
+			path = Generated;
+			sourceTree = "<group>";
+		};
+		1ABE07C61F0D86B300D36361 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				1ABE07DA1F0D915100D36361 /* Localizable.strings */,
+				04399A021D1C2D9D00E99CD9 /* Images.xcassets */,
+			);
+			path = Resources;
+			sourceTree = "<group>";
+		};
 		5628B4191F0C358D008B1E11 /* Settings */ = {
 			isa = PBXGroup;
 			children = (
@@ -724,6 +747,7 @@
 		564C44571E8D7F68000F92B1 /* Constants */ = {
 			isa = PBXGroup;
 			children = (
+				1ABE07C51F0D862D00D36361 /* Generated */,
 				56BBC9E61EDE1DDF00CDAF8B /* Colors.swift */,
 				564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */,
 				564C445A1E8EA44E000F92B1 /* Durations.swift */,
@@ -736,7 +760,6 @@
 			children = (
 				56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */,
 				56BBC9A91ED7153800CDAF8B /* Global.strings */,
-				56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */,
 			);
 			name = Internationalization;
 			sourceTree = "<group>";
@@ -807,6 +830,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 04399A201D1C2D9D00E99CD9 /* Build configuration list for PBXNativeTarget "Ring" */;
 			buildPhases = (
+				1ABE07C41F0D779F00D36361 /* ⚙️ SwiftGen Code Generation */,
 				1ABE07C31F0C28E000D36361 /* ⚠️ Swiftlint Analysis */,
 				0273C3011E0C655900CF00BA /* ⚙️ Copy Frameworks */,
 				043999EF1D1C2D9D00E99CD9 /* Sources */,
@@ -896,7 +920,6 @@
 			hasScannedForEncodings = 0;
 			knownRegions = (
 				en,
-				Base,
 			);
 			mainGroup = 043999EA1D1C2D9D00E99CD9;
 			productRefGroup = 043999F41D1C2D9D00E99CD9 /* Products */;
@@ -916,18 +939,16 @@
 			buildActionMask = 2147483647;
 			files = (
 				5669A7FE1EA904E4003C7B93 /* TextCell.xib in Resources */,
-				56BBC9AC1ED7154300CDAF8B /* Smartlist.strings in Resources */,
-				56BBC9AA1ED7153800CDAF8B /* Global.strings in Resources */,
+				1ABE07DC1F0D915100D36361 /* Localizable.strings in Resources */,
 				56BBC9CE1EDC5E7000CDAF8B /* MessageAccessoryView.xib in Resources */,
-				04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */,
+				04399A031D1C2D9D00E99CD9 /* Images.xcassets in Resources */,
 				56BBC9D01EDC5E7000CDAF8B /* MessageCell.xib in Resources */,
+				1ABE07E01F0D91A800D36361 /* Main.storyboard in Resources */,
 				5669A7FA1EA904AF003C7B93 /* SwitchCell.xib in Resources */,
-				1ABE07C21F0C22FB00D36361 /* Main.storyboard in Resources */,
 				56BBC9B51ED7156500CDAF8B /* ConversationCell.xib in Resources */,
+				1ABE07DF1F0D91A800D36361 /* LaunchScreen.storyboard in Resources */,
 				5669A7FC1EA904D2003C7B93 /* TextFieldCell.xib in Resources */,
-				1ABE07C11F0C22FB00D36361 /* LaunchScreen.storyboard in Resources */,
 				1ABE07BC1F0C22CC00D36361 /* WalkthroughStoryboard.storyboard in Resources */,
-				56BBC9E11EDDCA5900CDAF8B /* Walkthrough.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -983,6 +1004,20 @@
 			shellPath = /bin/sh;
 			shellScript = "if which swiftlint > /dev/null; then\n    swiftlint\nelse\n    echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
 		};
+		1ABE07C41F0D779F00D36361 /* ⚙️ SwiftGen Code Generation */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "⚙️ SwiftGen Code Generation";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "${PROJECT_DIR}/swiftgen/swiftgen.sh";
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -1006,11 +1041,13 @@
 				56BBC9E31EDDCC8100CDAF8B /* ConversationSection.swift in Sources */,
 				56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */,
 				5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */,
+				1ABE07D31F0D8FE800D36361 /* Storyboards.swift in Sources */,
 				56559B141EE89E7900BF20E1 /* DeviceModel.swift in Sources */,
 				56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */,
 				5628B4211F0C35C8008B1E11 /* WelcomeViewController.swift in Sources */,
 				5628B41C1F0C358D008B1E11 /* AccountDetailsViewController.swift in Sources */,
 				56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */,
+				1ABE07E21F0D924700D36361 /* Strings.swift in Sources */,
 				56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */,
 				56AC650E1E85694D00EA1AA9 /* RoundedTextField.swift in Sources */,
 				56BBC9BF1ED7168400CDAF8B /* SmartlistViewModel.swift in Sources */,
@@ -1029,6 +1066,7 @@
 				02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */,
 				564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */,
 				56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */,
+				1ABE07D21F0D8FE800D36361 /* Images.swift in Sources */,
 				56BBC9CD1EDC5E7000CDAF8B /* MessageAccessoryView.swift in Sources */,
 				02DD80CA1E1EAF1A009A3510 /* AccountCredentialsModel.swift in Sources */,
 				0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */,
@@ -1087,28 +1125,12 @@
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
-		1ABE07BD1F0C22FB00D36361 /* LaunchScreen.storyboard */ = {
+		1ABE07DA1F0D915100D36361 /* Localizable.strings */ = {
 			isa = PBXVariantGroup;
 			children = (
-				1ABE07BE1F0C22FB00D36361 /* Base */,
+				1ABE07DB1F0D915100D36361 /* en */,
 			);
-			name = LaunchScreen.storyboard;
-			sourceTree = "<group>";
-		};
-		1ABE07BF1F0C22FB00D36361 /* Main.storyboard */ = {
-			isa = PBXVariantGroup;
-			children = (
-				1ABE07C01F0C22FB00D36361 /* Base */,
-			);
-			name = Main.storyboard;
-			sourceTree = "<group>";
-		};
-		56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				56AC64DA1E8012CA00EA1AA9 /* Base */,
-			);
-			name = Walkthrough.strings;
+			name = Localizable.strings;
 			sourceTree = "<group>";
 		};
 /* End PBXVariantGroup section */
diff --git a/Ring/Ring/Base.lproj/Global.strings b/Ring/Ring/Base.lproj/Global.strings
deleted file mode 100644
index 0cc9f97..0000000
--- a/Ring/Ring/Base.lproj/Global.strings
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *  Copyright (C) 2017 Savoir-faire Linux Inc.
- *
- *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-"HomeTabBarTitle" = "Home";
diff --git a/Ring/Ring/Base.lproj/Smartlist.strings b/Ring/Ring/Base.lproj/Smartlist.strings
deleted file mode 100644
index 587e960..0000000
--- a/Ring/Ring/Base.lproj/Smartlist.strings
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *  Copyright (C) 2017 Savoir-faire Linux Inc.
- *
- *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-"Yesterday" = "Yesterday";
-
-"UserFound" = "User found";
-"Conversations" = "Conversations";
-
-"Searching" = "Searching...";
-"NoResults" = "No results";
-
diff --git a/Ring/Ring/Constants/Generated/Images.swift b/Ring/Ring/Constants/Generated/Images.swift
new file mode 100644
index 0000000..1203345
--- /dev/null
+++ b/Ring/Ring/Constants/Generated/Images.swift
@@ -0,0 +1,47 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/AliSoftware/SwiftGen
+
+#if os(iOS) || os(tvOS) || os(watchOS)
+  import UIKit.UIImage
+  public typealias Image = UIImage
+#elseif os(OSX)
+  import AppKit.NSImage
+  public typealias Image = NSImage
+#endif
+
+private class RingImagesBundleToken {}
+
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+
+// swiftlint:disable type_body_length
+public enum RingAsset: String {
+  case icContactPicture = "ic_contact_picture"
+  case logoRingBeta2Blanc = "logo-ring-beta2-blanc"
+
+  /** 
+    Loads from application's Bundle if image exists, then loads from current bundle, fatalError if image does not exist
+  */
+  public var smartImage: Image {
+    if let appimage = Image(named: self.rawValue, in: nil, compatibleWith: nil) {
+      return appimage
+    } else if let fmkImage = Image(named: self.rawValue, in: Bundle(for: RingImagesBundleToken.self), compatibleWith: nil) {
+      return fmkImage
+    } else {
+      fatalError("Impossible to load image \(self.rawValue)")
+    }
+  }
+
+  var image: Image {
+	if let img = Image(named: self.rawValue, in: Bundle(for: RingImagesBundleToken.self), compatibleWith: nil) {
+        return img
+    }
+    fatalError("Impossible to load image \(self.rawValue)")
+  }
+}
+// swiftlint:enable type_body_length
+
+public extension Image {
+  convenience init!(asset: RingAsset) {
+    self.init(named: asset.rawValue)
+  }
+}
diff --git a/Ring/Ring/Constants/Generated/Storyboards.swift b/Ring/Ring/Constants/Generated/Storyboards.swift
new file mode 100644
index 0000000..2e55c0e
--- /dev/null
+++ b/Ring/Ring/Constants/Generated/Storyboards.swift
@@ -0,0 +1,84 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen
+
+import Foundation
+import UIKit
+
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+// swiftlint:disable type_body_length
+
+protocol StoryboardSceneType {
+  static var storyboardName: String { get }
+}
+
+extension StoryboardSceneType {
+  static func storyboard() -> UIStoryboard {
+    return UIStoryboard(name: self.storyboardName, bundle: Bundle(for: BundleToken.self))
+  }
+
+  static func initialViewController() -> UIViewController {
+    guard let vc = storyboard().instantiateInitialViewController() else {
+      fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+    }
+    return vc
+  }
+}
+
+extension StoryboardSceneType where Self: RawRepresentable, Self.RawValue == String {
+  func viewController() -> UIViewController {
+    return Self.storyboard().instantiateViewController(withIdentifier: self.rawValue)
+  }
+  static func viewController(identifier: Self) -> UIViewController {
+    return identifier.viewController()
+  }
+}
+
+protocol StoryboardSegueType: RawRepresentable { }
+
+extension UIViewController {
+  func perform<S: StoryboardSegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
+    performSegue(withIdentifier: segue.rawValue, sender: sender)
+  }
+}
+
+enum StoryboardScene {
+  enum LaunchScreen: StoryboardSceneType {
+    static let storyboardName = "LaunchScreen"
+  }
+  enum Main: StoryboardSceneType {
+    static let storyboardName = "Main"
+
+    static func initialViewController() -> Ring.MainTabBarViewController {
+      guard let vc = storyboard().instantiateInitialViewController() as? Ring.MainTabBarViewController else {
+        fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+      }
+      return vc
+    }
+  }
+  enum WalkthroughStoryboard: StoryboardSceneType {
+    static let storyboardName = "WalkthroughStoryboard"
+
+    static func initialViewController() -> UINavigationController {
+      guard let vc = storyboard().instantiateInitialViewController() as? UINavigationController else {
+        fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+      }
+      return vc
+    }
+  }
+}
+
+enum StoryboardSegue {
+  enum Main: String, StoryboardSegueType {
+    case showMessages = "ShowMessages"
+    case accountDetails
+  }
+  enum WalkthroughStoryboard: String, StoryboardSegueType {
+    case accountToPermissionsSegue = "AccountToPermissionsSegue"
+    case createProfileSegue = "CreateProfileSegue"
+    case linkDeviceToAccountSegue = "LinkDeviceToAccountSegue"
+    case profileToAccountSegue = "ProfileToAccountSegue"
+    case profileToLinkSegue = "ProfileToLinkSegue"
+  }
+}
+
+private final class BundleToken {}
diff --git a/Ring/Ring/Constants/Generated/Strings.swift b/Ring/Ring/Constants/Generated/Strings.swift
new file mode 100644
index 0000000..f5a7610
--- /dev/null
+++ b/Ring/Ring/Constants/Generated/Strings.swift
@@ -0,0 +1,113 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/AliSoftware/SwiftGen
+
+import Foundation
+
+private class RingStringsBundleToken {}
+
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+
+// swiftlint:disable type_body_length
+// swiftlint:disable nesting
+// swiftlint:disable variable_name
+// swiftlint:disable valid_docs
+
+enum L10n {
+  /// Account Added
+  static let accountAddedTitle = L10n.tr("AccountAddedTitle")
+  /// Can't find account
+  static let accountCannotBeFoundTitle = L10n.tr("AccountCannotBeFoundTitle")
+  /// The account couldn't be created.
+  static let accountDefaultErrorMessage = L10n.tr("AccountDefaultErrorMessage")
+  /// Unknown error
+  static let accountDefaultErrorTitle = L10n.tr("AccountDefaultErrorTitle")
+  /// Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity.
+  static let accountNoNetworkMessage = L10n.tr("AccountNoNetworkMessage")
+  /// Can't connect to the network
+  static let accountNoNetworkTitle = L10n.tr("AccountNoNetworkTitle")
+  /// Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct.
+  static let acountCannotBeFoundMessage = L10n.tr("AcountCannotBeFoundMessage")
+  /// Choose strong password you will remember to protect your Ring account.
+  static let chooseStrongPassword = L10n.tr("ChooseStrongPassword")
+  /// Conversations
+  static let conversations = L10n.tr("Conversations")
+  /// Create a Ring account
+  static let createAccount = L10n.tr("CreateAccount")
+  /// Create your Ring account
+  static let createAccountFormTitle = L10n.tr("CreateAccountFormTitle")
+  /// Enter new username
+  static let enterNewUsernamePlaceholder = L10n.tr("EnterNewUsernamePlaceholder")
+  /// Home
+  static let homeTabBarTitle = L10n.tr("HomeTabBarTitle")
+  /// Invalid username
+  static let invalidUsername = L10n.tr("InvalidUsername")
+  /// Link this device to an account
+  static let linkDeviceButton = L10n.tr("LinkDeviceButton")
+  /// Looking for username availability...
+  static let lookingForUsernameAvailability = L10n.tr("LookingForUsernameAvailability")
+  /// New Password
+  static let newPasswordPlaceholder = L10n.tr("NewPasswordPlaceholder")
+  /// No results
+  static let noResults = L10n.tr("NoResults")
+  /// 6 characters minimum
+  static let passwordCharactersNumberError = L10n.tr("PasswordCharactersNumberError")
+  /// Passwords do not match
+  static let passwordNotMatchingError = L10n.tr("PasswordNotMatchingError")
+  /// Register public username (experimental)
+  static let registerPublicUsername = L10n.tr("RegisterPublicUsername")
+  /// Repeat new password
+  static let repeatPasswordPlaceholder = L10n.tr("RepeatPasswordPlaceholder")
+  /// Searching...
+  static let searching = L10n.tr("Searching")
+  /// User found
+  static let userFound = L10n.tr("UserFound")
+  /// Username already taken
+  static let usernameAlreadyTaken = L10n.tr("UsernameAlreadyTaken")
+  /// Adding account
+  static let waitCreateAccountTitle = L10n.tr("WaitCreateAccountTitle")
+  /// A Ring account allows you to reach people securely in peer to peer through fully distributed network
+  static let welcomeText = L10n.tr("WelcomeText")
+  /// Welcome to Ring
+  static let welcomeTitle = L10n.tr("WelcomeTitle")
+  /// Yesterday
+  static let yesterday = L10n.tr("Yesterday")
+}
+
+	struct LocalizableString {
+        let key: String
+        let args: [CVarArg]
+
+        /**
+         Returns String from Current Bundle
+         */
+        public var string: String {
+            let format: String = NSLocalizedString(key, tableName: nil, bundle: Bundle(for: RingStringsBundleToken.self), value: "", comment: "")
+            return String(format: format, locale: Locale.current, arguments: args)
+        }
+
+        /**
+         Returns String translated from App's Bundle is found, otherwise from Current Bundle
+         */
+        public var smartString: String {
+            // Load from App's Bundle first
+            var format: String = NSLocalizedString(key, tableName: nil, bundle: Bundle.main, value: "", comment: "")
+            if format != "" && format != key {
+                return String(format: format, locale: Locale.current, arguments: args)
+            }
+            // Load from Current Bundle
+            format = NSLocalizedString(key, tableName: nil, bundle: Bundle(for: RingStringsBundleToken.self), value: "", comment: "")
+
+            return String(format: format, locale: Locale.current, arguments: args)
+        }
+    }
+
+extension L10n {
+  fileprivate static func tr(_ key: String, _ args: CVarArg...) -> LocalizableString {
+    return LocalizableString(key: key, args: args)
+  }
+}
+
+// swiftlint:enable type_body_length
+// swiftlint:enable nesting
+// swiftlint:enable variable_name
+// swiftlint:enable valid_docs
diff --git a/Ring/Ring/Assets.xcassets/AppIcon.appiconset/Contents.json b/Ring/Ring/Resources/Images.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from Ring/Ring/Assets.xcassets/AppIcon.appiconset/Contents.json
rename to Ring/Ring/Resources/Images.xcassets/AppIcon.appiconset/Contents.json
diff --git a/Ring/Ring/Assets.xcassets/Contents.json b/Ring/Ring/Resources/Images.xcassets/Contents.json
similarity index 100%
rename from Ring/Ring/Assets.xcassets/Contents.json
rename to Ring/Ring/Resources/Images.xcassets/Contents.json
diff --git a/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/ic_contact_picture.imageset/Contents.json
similarity index 100%
rename from Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/Contents.json
rename to Ring/Ring/Resources/Images.xcassets/ic_contact_picture.imageset/Contents.json
diff --git a/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/ic_contact_picture.png b/Ring/Ring/Resources/Images.xcassets/ic_contact_picture.imageset/ic_contact_picture.png
similarity index 100%
rename from Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/ic_contact_picture.png
rename to Ring/Ring/Resources/Images.xcassets/ic_contact_picture.imageset/ic_contact_picture.png
Binary files differ
diff --git a/Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/Contents.json
similarity index 100%
rename from Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/Contents.json
rename to Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/Contents.json
diff --git a/Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc.png b/Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc.png
similarity index 100%
rename from Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc.png
rename to Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc.png
Binary files differ
diff --git a/Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@2x.png b/Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@2x.png
similarity index 100%
rename from Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@2x.png
rename to Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@2x.png
Binary files differ
diff --git a/Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@3x.png b/Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@3x.png
similarity index 100%
rename from Ring/Ring/Assets.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@3x.png
rename to Ring/Ring/Resources/Images.xcassets/logo-ring-beta2-blanc.imageset/logo-ring-beta2-blanc@3x.png
Binary files differ
diff --git a/Ring/Ring/Base.lproj/LaunchScreen.storyboard b/Ring/Ring/Resources/LaunchScreen.storyboard
similarity index 100%
rename from Ring/Ring/Base.lproj/LaunchScreen.storyboard
rename to Ring/Ring/Resources/LaunchScreen.storyboard
diff --git a/Ring/Ring/Base.lproj/Main.storyboard b/Ring/Ring/Resources/Main.storyboard
similarity index 100%
rename from Ring/Ring/Base.lproj/Main.storyboard
rename to Ring/Ring/Resources/Main.storyboard
diff --git a/Ring/Ring/Base.lproj/Walkthrough.strings b/Ring/Ring/Resources/en.lproj/Localizable.strings
similarity index 91%
rename from Ring/Ring/Base.lproj/Walkthrough.strings
rename to Ring/Ring/Resources/en.lproj/Localizable.strings
index 69c0b6c..b95453e 100644
--- a/Ring/Ring/Base.lproj/Walkthrough.strings
+++ b/Ring/Ring/Resources/en.lproj/Localizable.strings
@@ -18,6 +18,20 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
+// Global
+"HomeTabBarTitle" = "Home";
+
+// Smartlist
+"Yesterday" = "Yesterday";
+
+"UserFound" = "User found";
+"Conversations" = "Conversations";
+
+"Searching" = "Searching...";
+"NoResults" = "No results";
+
+// Walkthrough
+
 //Welcome Screen
 
 "WelcomeTitle" = "Welcome to Ring";
diff --git a/Ring/swiftgen/images.stencil b/Ring/swiftgen/images.stencil
new file mode 100644
index 0000000..fbefe90
--- /dev/null
+++ b/Ring/swiftgen/images.stencil
@@ -0,0 +1,52 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/AliSoftware/SwiftGen
+
+{% if images %}
+#if os(iOS) || os(tvOS) || os(watchOS)
+  import UIKit.UIImage
+  public typealias Image = UIImage
+#elseif os(OSX)
+  import AppKit.NSImage
+  public typealias Image = NSImage
+#endif
+
+private class RingImagesBundleToken {}
+
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+
+// swiftlint:disable type_body_length
+public enum Ring{{enumName}}: String {
+  {% for image in images %}
+  case {{image|swiftIdentifier|snakeToCamelCase|lowerFirstWord}} = "{{image}}"
+  {% endfor %}
+
+  /** 
+    Loads from application's Bundle if image exists, then loads from current bundle, fatalError if image does not exist
+  */
+  public var smartImage: Image {
+    if let appimage = Image(named: self.rawValue, in: nil, compatibleWith: nil) {
+      return appimage
+    } else if let fmkImage = Image(named: self.rawValue, in: Bundle(for: RingImagesBundleToken.self), compatibleWith: nil) {
+      return fmkImage
+    } else {
+      fatalError("Impossible to load image \(self.rawValue)")
+    }
+  }
+
+  var image: Image {
+	if let img = Image(named: self.rawValue, in: Bundle(for: RingImagesBundleToken.self), compatibleWith: nil) {
+        return img
+    }
+    fatalError("Impossible to load image \(self.rawValue)")
+  }
+}
+// swiftlint:enable type_body_length
+
+public extension Image {
+  convenience init!(asset: Ring{{enumName}}) {
+    self.init(named: asset.rawValue)
+  }
+}
+{% else %}
+// No image found
+{% endif %}
diff --git a/Ring/swiftgen/storyboards.stencil b/Ring/swiftgen/storyboards.stencil
new file mode 100644
index 0000000..ad2d2fd
--- /dev/null
+++ b/Ring/swiftgen/storyboards.stencil
@@ -0,0 +1,120 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/SwiftGen/SwiftGen
+
+{% macro className scene %}{% if scene.customClass %}{% if scene.customModule %}{{scene.customModule}}.{% endif %}{{scene.customClass}}{% else %}UI{{scene.baseType}}{% endif %}{% endmacro %}
+import Foundation
+import UIKit
+{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
+import {{module}}
+{% endfor %}
+
+{% if storyboards %}
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+// swiftlint:disable type_body_length
+
+{# This first part of the code is static, same every time whatever Storyboard you have #}
+protocol StoryboardSceneType {
+  static var storyboardName: String { get }
+}
+
+extension StoryboardSceneType {
+  static func storyboard() -> UIStoryboard {
+    return UIStoryboard(name: self.storyboardName, bundle: Bundle(for: BundleToken.self))
+  }
+
+  static func initialViewController() -> UIViewController {
+    guard let vc = storyboard().instantiateInitialViewController() else {
+      fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+    }
+    return vc
+  }
+}
+
+extension StoryboardSceneType where Self: RawRepresentable, Self.RawValue == String {
+  func viewController() -> UIViewController {
+    return Self.storyboard().instantiateViewController(withIdentifier: self.rawValue)
+  }
+  static func viewController(identifier: Self) -> UIViewController {
+    return identifier.viewController()
+  }
+}
+
+protocol StoryboardSegueType: RawRepresentable { }
+
+extension UIViewController {
+  func perform<S: StoryboardSegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
+    performSegue(withIdentifier: segue.rawValue, sender: sender)
+  }
+}
+
+{# This is where the generation begins, this code depends on what you have in your Storyboards #}
+{% set sceneEnumName %}{{param.sceneEnumName|default:"StoryboardScene"}}{% endset %}
+enum {{sceneEnumName}} {
+  {% for storyboard in storyboards %}
+  {% set storyboardName %}{{storyboard.name|swiftIdentifier|escapeReservedKeywords}}{% endset %}
+  {% if storyboard.scenes %}
+  enum {{storyboardName}}: String, StoryboardSceneType {
+    static let storyboardName = "{{storyboard.name}}"
+    {% if storyboard.initialScene and storyboard.initialScene.baseType != "ViewController" %}
+    {% set initialSceneClass %}{% call className storyboard.initialScene %}{% endset %}
+
+    static func initialViewController() -> {{initialSceneClass}} {
+      guard let vc = storyboard().instantiateInitialViewController() as? {{initialSceneClass}} else {
+        fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+      }
+      return vc
+    }
+    {% endif %}
+    {% for scene in storyboard.scenes %}
+    {% set sceneID %}{{scene.identifier|swiftIdentifier|snakeToCamelCase|lowerFirstWord}}{% endset %}
+
+    case {{sceneID}}Scene = "{{scene.identifier}}"
+    {% if scene.baseType != "ViewController" %}
+    {% set sceneClass %}{% call className scene %}{% endset %}
+    static func instantiate{{sceneID|snakeToCamelCase}}() -> {{sceneClass}} {
+      guard let vc = {{sceneEnumName}}.{{storyboardName}}.{{sceneID}}Scene.viewController() as? {{sceneClass}}
+      else {
+        fatalError("ViewController '{{scene.identifier}}' is not of the expected class {{sceneClass}}.")
+      }
+      return vc
+    }
+    {% else %}
+    static func instantiate{{sceneID|snakeToCamelCase}}() -> UIViewController {
+      return {{sceneEnumName}}.{{storyboardName}}.{{sceneID}}Scene.viewController()
+    }
+    {% endif %}
+    {% endfor %}
+  }
+  {% else %}
+  enum {{storyboardName}}: StoryboardSceneType {
+    static let storyboardName = "{{storyboard.name}}"
+    {% if storyboard.initialScene and storyboard.initialScene.baseType != "ViewController" %}
+    {% set initialSceneClass %}{% call className storyboard.initialScene %}{% endset %}
+
+    static func initialViewController() -> {{initialSceneClass}} {
+      guard let vc = storyboard().instantiateInitialViewController() as? {{initialSceneClass}} else {
+        fatalError("Failed to instantiate initialViewController for \(self.storyboardName)")
+      }
+      return vc
+    }
+    {% endif %}
+  }
+  {% endif %}
+  {% endfor %}
+}
+
+enum {{param.segueEnumName|default:"StoryboardSegue"}} {
+  {% for storyboard in storyboards where storyboard.segues %}
+  enum {{storyboard.name|swiftIdentifier|escapeReservedKeywords}}: String, StoryboardSegueType {
+    {% for segue in storyboard.segues %}
+    {% set segueID %}{{segue.identifier|swiftIdentifier|snakeToCamelCase|lowerFirstWord}}{% endset %}
+    case {{segueID|escapeReservedKeywords}}{% if segueID != segue.identifier %} = "{{segue.identifier}}"{% endif %}
+    {% endfor %}
+  }
+  {% endfor %}
+}
+
+private final class BundleToken {}
+{% else %}
+// No storyboard found
+{% endif %}
diff --git a/Ring/swiftgen/strings.stencil b/Ring/swiftgen/strings.stencil
new file mode 100644
index 0000000..d7b96e3
--- /dev/null
+++ b/Ring/swiftgen/strings.stencil
@@ -0,0 +1,83 @@
+// Generated using SwiftGen, by O.Halligon — https://github.com/AliSoftware/SwiftGen
+
+{% if structuredStrings %}
+import Foundation
+
+private class RingStringsBundleToken {}
+
+// swiftlint:disable file_length
+// swiftlint:disable line_length
+
+// swiftlint:disable type_body_length
+// swiftlint:disable nesting
+// swiftlint:disable variable_name
+// swiftlint:disable valid_docs
+{% macro parametersBlock params %}{% for type in params.types %}_ p{{forloop.counter}}: {{type}}{% if not forloop.last %}, {% endif %}{% endfor %}{% endmacro %}
+{% macro argumentsBlock params %}{% for type in params.types %}p{{forloop.counter}}{% if not forloop.last %}, {% endif %}{% endfor %}{% endmacro %}
+{% macro recursiveBlock strings sp %}
+{{sp}}  {% for string in strings.strings %}
+{{sp}}  /// {{string.translation}}
+{{sp}}  {% if string.params %}
+{{sp}}  static func {{string.keytail|swiftIdentifier|snakeToCamelCase|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.params %}) -> LocalizableString {
+{{sp}}    return {{enumName}}.tr("{{string.key}}", {% call argumentsBlock string.params %})
+{{sp}}  }
+{{sp}}  {% else %}
+{{sp}}  static let {{string.keytail|swiftIdentifier|snakeToCamelCase|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{string.key}}")
+{{sp}}  {% endif %}
+{{sp}}  {% endfor %}
+{{sp}}  {% if strings.subenums %}
+{{sp}}  {% for subenum in strings.subenums %}
+
+{{sp}}  enum {{subenum.name|swiftIdentifier|snakeToCamelCase}} {
+{{sp}}    {% set sp2 %}{{sp}}  {% endset %}
+{{sp}}    {% call recursiveBlock subenum sp2 %}
+{{sp}}  }
+{{sp}}  {% endfor %}
+{{sp}}  {% endif %}
+{% endmacro %}
+
+enum {{enumName}} {
+  {% call recursiveBlock structuredStrings sp %}
+}
+
+	struct LocalizableString {
+        let key: String
+        let args: [CVarArg]
+
+        /**
+         Returns String from Current Bundle
+         */
+        public var string: String {
+            let format: String = NSLocalizedString(key, tableName: nil, bundle: Bundle(for: RingStringsBundleToken.self), value: "", comment: "")
+            return String(format: format, locale: Locale.current, arguments: args)
+        }
+
+        /**
+         Returns String translated from App's Bundle is found, otherwise from Current Bundle
+         */
+        public var smartString: String {
+            // Load from App's Bundle first
+            var format: String = NSLocalizedString(key, tableName: nil, bundle: Bundle.main, value: "", comment: "")
+            if format != "" && format != key {
+                return String(format: format, locale: Locale.current, arguments: args)
+            }
+            // Load from Current Bundle
+            format = NSLocalizedString(key, tableName: nil, bundle: Bundle(for: RingStringsBundleToken.self), value: "", comment: "")
+
+            return String(format: format, locale: Locale.current, arguments: args)
+        }
+    }
+
+extension {{enumName}} {
+  fileprivate static func tr(_ key: String, _ args: CVarArg...) -> LocalizableString {
+    return LocalizableString(key: key, args: args)
+  }
+}
+
+// swiftlint:enable type_body_length
+// swiftlint:enable nesting
+// swiftlint:enable variable_name
+// swiftlint:enable valid_docs
+{% else %}
+// No string found
+{% endif %}
diff --git a/Ring/swiftgen/swiftgen.sh b/Ring/swiftgen/swiftgen.sh
new file mode 100755
index 0000000..0c4da0b
--- /dev/null
+++ b/Ring/swiftgen/swiftgen.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+EXPECTED_VERSION="SwiftGen v4.2.1 (Stencil v0.9.0, StencilSwiftKit v1.0.2, SwiftGenKit v1.1.0)"
+
+# Here execute the various SwiftGen commands you need
+run_swiftgen() {
+	if [ ! "$PROJECT_DIR" -o ! $"PROJECT_NAME" ]; then echo "Some variables are not set. Please run from an Xcode build phase"; exit 1; fi
+	SRCDIR="$PROJECT_DIR/$PROJECT_NAME"
+	OUTDIR="$SRCDIR/Constants/Generated"
+	TPLDIR=$(dirname $0)
+
+	echo "SwiftGen: Generating files..."
+	swiftgen storyboards "$SRCDIR" -p "$TPLDIR/storyboards.stencil" --output "$OUTDIR/Storyboards.swift"
+	swiftgen images "$SRCDIR/Resources/Images.xcassets" -p "$TPLDIR/images.stencil" --output "$OUTDIR/Images.swift"
+	swiftgen strings "$SRCDIR/Resources/en.lproj/Localizable.strings" -p "$TPLDIR/strings.stencil" --output "$OUTDIR/Strings.swift"
+}
+
+
+
+# Main script to check if SwiftGen is installed, check the version, and run it only if version matches
+if which swiftgen >/dev/null; then
+	CURRENT_VERSION=`swiftgen --version`
+	if [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
+		echo "error: SwiftGen version mismatch (expected ${EXPECTED_VERSION%% \(*\)}, got ${CURRENT_VERSION%% \(*\)})"
+		exit 1
+	fi
+
+	run_swiftgen
+else
+	echo "warning: SwiftGen not installed, download it from https://github.com/SwiftGen/SwiftGen"
+fi