diff --git a/.gitignore b/.gitignore
index ce934a9..2eee4f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ xcuserdata
# Build products
build/
+.build/
*.o
*.LinkFileList
*.hmap
@@ -30,9 +31,6 @@ build/
*.dat
*.dep
-# Cocoapods
-Pods
-
# AppCode specific files
.idea/
*.iml
@@ -43,5 +41,9 @@ fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
+# Config files
Secrets.h
.env
+
+# Generated files
+Sources/Tropos/Resources/Settings.bundle/Acknowledgements*
diff --git a/.ruby-version b/.ruby-version
deleted file mode 100644
index 437459c..0000000
--- a/.ruby-version
+++ /dev/null
@@ -1 +0,0 @@
-2.5.0
diff --git a/Cartfile b/Cartfile
index 4b660e1..a3af15d 100644
--- a/Cartfile
+++ b/Cartfile
@@ -1,3 +1,3 @@
-github "ReactiveCocoa/ReactiveObjCBridge"
+github "ReactiveCocoa/ReactiveObjCBridge" "as-ras-6.1.0"
github "ReactiveCocoa/ReactiveSwift"
github "mixpanel/mixpanel-iphone"
diff --git a/Cartfile.resolved b/Cartfile.resolved
index f2c3fb9..19d1626 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -1,8 +1,7 @@
-github "AliSoftware/OHHTTPStubs" "6.1.0"
-github "Quick/Nimble" "v8.0.1"
-github "Quick/Quick" "v1.3.0"
-github "ReactiveCocoa/ReactiveObjC" "3.1.0"
-github "ReactiveCocoa/ReactiveObjCBridge" "3.1.0"
-github "ReactiveCocoa/ReactiveSwift" "3.1.0"
-github "antitypical/Result" "3.2.4"
-github "mixpanel/mixpanel-iphone" "v3.3.7"
+github "AliSoftware/OHHTTPStubs" "8.0.0"
+github "Quick/Nimble" "v8.0.2"
+github "Quick/Quick" "v2.1.0"
+github "ReactiveCocoa/ReactiveObjC" "3.1.1"
+github "ReactiveCocoa/ReactiveObjCBridge" "c1c49ea807d783313f696184b55956094c993e2d"
+github "ReactiveCocoa/ReactiveSwift" "6.1.0"
+github "mixpanel/mixpanel-iphone" "v3.4.7"
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..0bf7833
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,11 @@
+// swift-tools-version:4.2
+
+import PackageDescription
+
+let package = Package(
+ name: "Tropos",
+ targets: [
+ .target(name: "generate-acknowledgements", dependencies: ["Settings"]),
+ .target(name: "Settings"),
+ ]
+)
diff --git a/Sources/Settings/Bundle.swift b/Sources/Settings/Bundle.swift
new file mode 100644
index 0000000..145d348
--- /dev/null
+++ b/Sources/Settings/Bundle.swift
@@ -0,0 +1,9 @@
+import class Foundation.Bundle
+
+extension Bundle {
+ public var settingsBundle: Bundle? {
+ return url(forResource: "Settings", withExtension: "bundle").flatMap {
+ Bundle(url: $0)
+ }
+ }
+}
diff --git a/Sources/Settings/PreferencePage.swift b/Sources/Settings/PreferencePage.swift
new file mode 100644
index 0000000..4efe596
--- /dev/null
+++ b/Sources/Settings/PreferencePage.swift
@@ -0,0 +1,13 @@
+public struct PreferencePage {
+ public var preferenceSpecifiers: [PreferenceSpecifier]
+
+ public init(preferenceSpecifiers: [PreferenceSpecifier] = []) {
+ self.preferenceSpecifiers = preferenceSpecifiers
+ }
+}
+
+extension PreferencePage: Codable {
+ private enum CodingKeys: String, CodingKey {
+ case preferenceSpecifiers = "PreferenceSpecifiers"
+ }
+}
diff --git a/Sources/Settings/PreferenceSpecifier.swift b/Sources/Settings/PreferenceSpecifier.swift
new file mode 100644
index 0000000..cb2b48b
--- /dev/null
+++ b/Sources/Settings/PreferenceSpecifier.swift
@@ -0,0 +1,19 @@
+public struct PreferenceSpecifier {
+ public var file: String?
+ public var footerText: String?
+ public var title: String?
+ public let type: String
+
+ public init(type: String) {
+ self.type = type
+ }
+}
+
+extension PreferenceSpecifier: Codable {
+ private enum CodingKeys: String, CodingKey {
+ case file = "File"
+ case footerText = "FooterText"
+ case title = "Title"
+ case type = "Type"
+ }
+}
diff --git a/Sources/Tropos/Extensions/Optional.swift b/Sources/Tropos/Extensions/Optional.swift
new file mode 100644
index 0000000..769a722
--- /dev/null
+++ b/Sources/Tropos/Extensions/Optional.swift
@@ -0,0 +1,9 @@
+extension Optional {
+ func unwrap(file: StaticString = #file, line: UInt = #line) throws -> Wrapped {
+ if let value = self {
+ return value
+ } else {
+ throw NilError(file: file, line: line)
+ }
+ }
+}
diff --git a/Sources/Tropos/Models/NilError.swift b/Sources/Tropos/Models/NilError.swift
new file mode 100644
index 0000000..b91cc5e
--- /dev/null
+++ b/Sources/Tropos/Models/NilError.swift
@@ -0,0 +1,20 @@
+import Foundation
+
+struct NilError: Error, CustomNSError {
+ static let lineNumberKey = "line-number"
+
+ let file: StaticString
+ let line: UInt
+
+ var errorDescription: String? {
+ return "Unexpectedly found nil while unwrapping optional"
+ }
+
+ var errorUserInfo: [String: Any] {
+ return [
+ NSFilePathErrorKey: file,
+ NSLocalizedDescriptionKey: errorDescription!,
+ NilError.lineNumberKey: line,
+ ]
+ }
+}
diff --git a/Sources/Tropos/Resources/Settings.bundle/Root.plist b/Sources/Tropos/Resources/Settings.bundle/Root.plist
new file mode 100644
index 0000000..654f6ce
--- /dev/null
+++ b/Sources/Tropos/Resources/Settings.bundle/Root.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ StringsTable
+ Root
+ PreferenceSpecifiers
+
+
+ Type
+ PSChildPaneSpecifier
+ File
+ Acknowledgements
+ Title
+ Acknowledgements
+
+
+
+
diff --git a/Sources/Tropos/Resources/Settings.bundle/en.lproj/Root.strings b/Sources/Tropos/Resources/Settings.bundle/en.lproj/Root.strings
new file mode 100644
index 0000000..8cd87b9
Binary files /dev/null and b/Sources/Tropos/Resources/Settings.bundle/en.lproj/Root.strings differ
diff --git a/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard b/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard
index fb27780..5c13e5e 100644
--- a/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard
+++ b/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard
@@ -12,6 +12,9 @@
DINNextLTPro-Light
+
+ DINNextLTPro-Regular
+
@@ -21,6 +24,7 @@
+
@@ -124,7 +128,7 @@
-
+
@@ -234,9 +238,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -246,7 +292,7 @@
-
+
@@ -254,12 +300,12 @@
-
+
-
-
-
-
+
+
+
+
@@ -268,7 +314,7 @@
-
+
@@ -306,7 +352,7 @@
-
+
@@ -387,7 +433,7 @@
-
+
@@ -396,7 +442,7 @@