From 1610f419bdbd784f369c1b79dbd752fb7182d541 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 4 Jan 2019 12:33:01 -0500 Subject: [PATCH 01/10] Fix acknowledgements text being clipped by navigation bar --- .../Storyboards/Base.lproj/Main.storyboard | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard b/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard index fb27780..4a38ae2 100644 --- a/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard +++ b/Sources/Tropos/Resources/Storyboards/Base.lproj/Main.storyboard @@ -246,7 +246,7 @@ - + @@ -254,12 +254,12 @@ - + - - - - + + + + @@ -268,7 +268,7 @@ - + @@ -387,7 +387,7 @@ - + @@ -396,7 +396,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -268,7 +314,7 @@ - + @@ -306,7 +352,7 @@ - + diff --git a/Sources/Tropos/ViewControllers/AcknowledgementsViewController.swift b/Sources/Tropos/ViewControllers/AcknowledgementsViewController.swift new file mode 100644 index 0000000..67684ee --- /dev/null +++ b/Sources/Tropos/ViewControllers/AcknowledgementsViewController.swift @@ -0,0 +1,79 @@ +import os.log +import Settings +import UIKit + +final class AcknowledgementsViewController: UITableViewController { + private var acknowledgements = PreferencePage() + private var settings = Bundle.main.settingsBundle! + + override func viewDidLoad() { + super.viewDidLoad() + + do { + acknowledgements = try loadAcknowledgementsList() + } catch { + guard #available(iOS 10.0, *) else { return } + os_log("Failed to load acknowledgements: %{public}@", type: .error, error.localizedDescription) + } + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return acknowledgements.preferenceSpecifiers.count + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return NSLocalizedString("Third-Party Code", comment: "Title for Acknowledgements library list") + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let library = acknowledgements.preferenceSpecifiers[indexPath.row] + let cell = tableView.dequeueReusableCell(withIdentifier: "Library", for: indexPath) + cell.textLabel?.text = library.title + return cell + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + defer { super.prepare(for: segue, sender: sender) } + + guard let destination = segue.destination as? TextViewController, + let indexPath = (sender as? UITableViewCell).flatMap(tableView.indexPath(for:)) + else { return } + + let library = acknowledgements.preferenceSpecifiers[indexPath.row] + destination.title = library.title + + do { + let libraryAcknowledgements = try loadAcknowledgements(forLibraryNamed: library.title.unwrap()) + destination.text = libraryAcknowledgements.footerText + } catch { + guard #available(iOS 10.0, *) else { return } + + os_log( + "Failed to load acknowledgements for library '%{public}@': %{public}@", + type: .error, + library.title ?? "nil", + error.localizedDescription + ) + } + } +} + +private extension AcknowledgementsViewController { + func loadAcknowledgementsList() throws -> PreferencePage { + return try settings + .url(forResource: "Acknowledgements", withExtension: "plist") + .map { try Data(contentsOf: $0) } + .map { try PropertyListDecoder().decode(PreferencePage.self, from: $0) } + .unwrap() + } + + func loadAcknowledgements(forLibraryNamed libraryName: String) throws -> PreferenceSpecifier { + let page = try settings + .url(forResource: libraryName, withExtension: "plist", subdirectory: "Acknowledgements") + .map { try Data(contentsOf: $0) } + .map { try PropertyListDecoder().decode(PreferencePage.self, from: $0) } + .unwrap() + + return try page.preferenceSpecifiers.first.unwrap() + } +} diff --git a/Sources/Tropos/ViewControllers/SettingsTableViewController.swift b/Sources/Tropos/ViewControllers/SettingsTableViewController.swift index 1ed8e0a..5789c8a 100644 --- a/Sources/Tropos/ViewControllers/SettingsTableViewController.swift +++ b/Sources/Tropos/ViewControllers/SettingsTableViewController.swift @@ -3,7 +3,7 @@ import UIKit private enum SegueIdentifier: String { case privacyPolicy = "ShowWebViewController" - case acknowledgements = "ShowTextViewController" + case acknowledgements = "ShowAcknowledgementsViewController" init?(identifier: String?) { guard let identifier = identifier else { return nil } @@ -76,15 +76,7 @@ class SettingsTableViewController: UITableViewController { case .privacyPolicy?: let webViewController = segue.destination as? WebViewController webViewController?.url = URL(string: "http://www.troposweather.com/privacy/")! - case .acknowledgements?: - let textViewController = segue.destination as? TextViewController - let fileURL = Bundle.main.url( - forResource: "Pods-Tropos-settings-metadata", - withExtension: "plist" - ) - let parser = fileURL.flatMap { AcknowledgementsParser(fileURL: $0) } - textViewController?.text = parser?.displayString() - case nil: + default: break } } diff --git a/Tropos.xcodeproj/project.pbxproj b/Tropos.xcodeproj/project.pbxproj index f09be64..c8abd33 100644 --- a/Tropos.xcodeproj/project.pbxproj +++ b/Tropos.xcodeproj/project.pbxproj @@ -100,6 +100,14 @@ 4ABCA2C320CAD0B300C46C4C /* TroposCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A552EEF1CC7C8F8008C7298 /* TroposCore.framework */; }; 4AC287A41CBC3D800064F48A /* Nimble.framework in Embed Carthage Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0F135A1B432976001685BA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4AC287A51CBC3D820064F48A /* Quick.framework in Embed Carthage Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0F135B1B432976001685BA /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4ACA4F1921DFB69100741046 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 4ACA4F1821DFB69100741046 /* Settings.bundle */; }; + 4ACA4F1B21DFD2ED00741046 /* AcknowledgementsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F1A21DFD2ED00741046 /* AcknowledgementsViewController.swift */; }; + 4ACA4F2321E0019400741046 /* PreferencePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F2221E0019400741046 /* PreferencePage.swift */; }; + 4ACA4F2721E0020900741046 /* libSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ACA4F2021E0019400741046 /* libSettings.a */; }; + 4ACA4F2B21E0025100741046 /* PreferenceSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F2A21E0025100741046 /* PreferenceSpecifier.swift */; }; + 4ACA4F2D21E002C000741046 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F2C21E002C000741046 /* Bundle.swift */; }; + 4ACA4F2F21E0152000741046 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F2E21E0152000741046 /* Optional.swift */; }; + 4ACA4F3121E0166200741046 /* NilError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA4F3021E0166200741046 /* NilError.swift */; }; 4ACA584B1CC526D600DD0CDC /* RACSignal+TROperators.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ACA584A1CC526D600DD0CDC /* RACSignal+TROperators.m */; }; 4AFCB1CF21D67AD100176FDD /* ForecastController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFCB1CD21D67A7600176FDD /* ForecastController.swift */; }; 4AFCB1D121D67F2000176FDD /* Date+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AFCB1D021D67F2000176FDD /* Date+Relative.swift */; }; @@ -172,6 +180,13 @@ remoteGlobalIDString = 4A552EEE1CC7C8F8008C7298; remoteInfo = TroposCore; }; + 4ACA4F2821E0022900741046 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 198EF8739DFBDBC4FBC67D3B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4ACA4F1F21E0019400741046; + remoteInfo = Settings; + }; A1FC9ED41CFEA23B00E62618 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 198EF8739DFBDBC4FBC67D3B /* Project object */; @@ -222,6 +237,15 @@ name = "Embed Carthage Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 4ACA4F1E21E0019400741046 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A1FC9ED61CFEA23B00E62618 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -327,6 +351,14 @@ 4AA138D821348E360083B816 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/Intents.strings"; sourceTree = ""; }; 4ABCA2C420CAD14C00C46C4C /* CheckWeatherIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckWeatherIntentHandler.swift; sourceTree = ""; }; 4ABCA2CC20CB1F5500C46C4C /* TroposIntents.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TroposIntents.entitlements; sourceTree = ""; }; + 4ACA4F1821DFB69100741046 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + 4ACA4F1A21DFD2ED00741046 /* AcknowledgementsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcknowledgementsViewController.swift; sourceTree = ""; }; + 4ACA4F2021E0019400741046 /* libSettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSettings.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4ACA4F2221E0019400741046 /* PreferencePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencePage.swift; sourceTree = ""; }; + 4ACA4F2A21E0025100741046 /* PreferenceSpecifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceSpecifier.swift; sourceTree = ""; }; + 4ACA4F2C21E002C000741046 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; + 4ACA4F2E21E0152000741046 /* Optional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; + 4ACA4F3021E0166200741046 /* NilError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NilError.swift; sourceTree = ""; }; 4ACA58491CC526D600DD0CDC /* RACSignal+TROperators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+TROperators.h"; sourceTree = ""; }; 4ACA584A1CC526D600DD0CDC /* RACSignal+TROperators.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+TROperators.m"; sourceTree = ""; }; 4AEF9E872040C47400CDDEEA /* GeocodeControllerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeocodeControllerSpec.swift; sourceTree = ""; }; @@ -439,10 +471,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4ACA4F1D21E0019400741046 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 800364D174D6FAD56EBFE31C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4ACA4F2721E0020900741046 /* libSettings.a in Frameworks */, 4AFCB1E921D6C32100176FDD /* AppCenter.framework in Frameworks */, 4AFCB1EA21D6C32100176FDD /* AppCenterCrashes.framework in Frameworks */, A1FC9ED21CFEA23B00E62618 /* TroposCore.framework in Frameworks */, @@ -492,6 +532,7 @@ C2E44CEB1A3A42C6009CC844 /* InfoPlist.strings */, C2E44CE61A3A4273009CC844 /* Localizable.strings */, 3AC958D858D40E8D581F9DD8 /* Nibs */, + 4ACA4F1821DFB69100741046 /* Settings.bundle */, 7786B6CD5802E36505CA8896 /* Storyboards */, ); path = Resources; @@ -556,6 +597,7 @@ 4A21D76020C9E1C10055A2AF /* Sources */ = { isa = PBXGroup; children = ( + 4ACA4F2121E0019400741046 /* Settings */, 4A0DD7521CCA968500E7A451 /* Tropos */, 4A552F401CC7CB9B008C7298 /* TroposCore */, 4A21D76620C9E6FA0055A2AF /* TroposIntents */, @@ -768,6 +810,16 @@ path = Protocols; sourceTree = ""; }; + 4ACA4F2121E0019400741046 /* Settings */ = { + isa = PBXGroup; + children = ( + 4ACA4F2C21E002C000741046 /* Bundle.swift */, + 4ACA4F2221E0019400741046 /* PreferencePage.swift */, + 4ACA4F2A21E0025100741046 /* PreferenceSpecifier.swift */, + ); + path = Settings; + sourceTree = ""; + }; 4D0A5C3E1A3A578E0084C41E /* Fonts */ = { isa = PBXGroup; children = ( @@ -809,6 +861,7 @@ 4A552EEF1CC7C8F8008C7298 /* TroposCore.framework */, 4A552EF81CC7C8F8008C7298 /* TroposCoreSpecs.xctest */, 4A21D76520C9E6FA0055A2AF /* TroposIntents.appex */, + 4ACA4F2021E0019400741046 /* libSettings.a */, ); name = Products; sourceTree = ""; @@ -853,6 +906,7 @@ isa = PBXGroup; children = ( A12FCE241D336FA90062E7F1 /* Color.swift */, + 4ACA4F3021E0166200741046 /* NilError.swift */, ); path = Models; sourceTree = ""; @@ -860,6 +914,7 @@ C2E44CDE1A3A3BFB009CC844 /* ViewControllers */ = { isa = PBXGroup; children = ( + 4ACA4F1A21DFD2ED00741046 /* AcknowledgementsViewController.swift */, 4D115AF11ADC8C0C0032AAA3 /* SettingsTableViewController.swift */, A12FCE271D3389A10062E7F1 /* TextViewController.swift */, C2E44CDF1A3A3C09009CC844 /* TRWeatherViewController.h */, @@ -896,6 +951,7 @@ children = ( 51FEC99B21CD9E4A00CDBC97 /* CATransition.swift */, 4A5109D420C9CB8100F993FC /* INInteraction.swift */, + 4ACA4F2E21E0152000741046 /* Optional.swift */, A10108F71D3369EB0024B1BA /* NSBundle+TRBundleInfo.h */, A10108F81D3369EB0024B1BA /* NSBundle+TRBundleInfo.m */, 4D7CDEBD1A46DD030038DD33 /* NSError+TRErrors.h */, @@ -956,6 +1012,7 @@ buildRules = ( ); dependencies = ( + 4ACA4F2921E0022900741046 /* PBXTargetDependency */, A1FC9ED51CFEA23B00E62618 /* PBXTargetDependency */, 4A21D76B20C9E6FB0055A2AF /* PBXTargetDependency */, ); @@ -1020,6 +1077,23 @@ productReference = 4A552EF81CC7C8F8008C7298 /* TroposCoreSpecs.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 4ACA4F1F21E0019400741046 /* Settings */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4ACA4F2421E0019400741046 /* Build configuration list for PBXNativeTarget "Settings" */; + buildPhases = ( + 4ACA4F1C21E0019400741046 /* Sources */, + 4ACA4F1D21E0019400741046 /* Frameworks */, + 4ACA4F1E21E0019400741046 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Settings; + productName = Settings; + productReference = 4ACA4F2021E0019400741046 /* libSettings.a */; + productType = "com.apple.product-type.library.static"; + }; AA349281EBCAE02432E93D8E /* TroposTests */ = { isa = PBXNativeTarget; buildConfigurationList = 2F9F5A62DE2B467BF3AC0803 /* Build configuration list for PBXNativeTarget "TroposTests" */; @@ -1047,7 +1121,7 @@ attributes = { CLASSPREFIX = TR; LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 1000; + LastSwiftUpdateCheck = 1010; LastUpgradeCheck = 0940; ORGANIZATIONNAME = thoughtbot; TargetAttributes = { @@ -1090,6 +1164,11 @@ LastSwiftMigration = 0920; TestTargetID = 45ADD324EABFD89F9E640007; }; + 4ACA4F1F21E0019400741046 = { + CreatedOnToolsVersion = 10.1; + DevelopmentTeam = 8E7TQ638ZD; + ProvisioningStyle = Automatic; + }; AA349281EBCAE02432E93D8E = { DevelopmentTeam = 8E7TQ638ZD; LastSwiftMigration = 0920; @@ -1118,6 +1197,7 @@ AA349281EBCAE02432E93D8E /* TroposTests */, 4A552EEE1CC7C8F8008C7298 /* TroposCore */, 4A552EF71CC7C8F8008C7298 /* TroposCoreTests */, + 4ACA4F1F21E0019400741046 /* Settings */, ); }; /* End PBXProject section */ @@ -1141,6 +1221,7 @@ 4D3336F61A80BE16001BA9A8 /* TRDailyForecastView.xib in Resources */, C2E44CE41A3A4273009CC844 /* Localizable.strings in Resources */, 5DA50AD91A82CAB300CAE666 /* DINNextLTPro-Light.otf in Resources */, + 4ACA4F1921DFB69100741046 /* Settings.bundle in Resources */, A12FCE2A1D338A4A0062E7F1 /* DINNextLTPro-Regular.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1320,6 +1401,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4ACA4F1C21E0019400741046 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4ACA4F2321E0019400741046 /* PreferencePage.swift in Sources */, + 4ACA4F2D21E002C000741046 /* Bundle.swift in Sources */, + 4ACA4F2B21E0025100741046 /* PreferenceSpecifier.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7C2C287E78ABB83892B5E8B6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1335,6 +1426,7 @@ 4A86FB65205C0E4D00E761C3 /* AppDelegate.swift in Sources */, A10109001D336C9C0024B1BA /* TRTableViewCell.swift in Sources */, 4D49554C1A8C298A0066F278 /* TRRefreshView.m in Sources */, + 4ACA4F2F21E0152000741046 /* Optional.swift in Sources */, A12FCE251D336FA90062E7F1 /* Color.swift in Sources */, 4A5109D520C9CB8100F993FC /* INInteraction.swift in Sources */, A1F869201D3357D6003A826D /* AppearanceController.swift in Sources */, @@ -1344,10 +1436,12 @@ A12FCE281D3389A10062E7F1 /* TextViewController.swift in Sources */, A1628AE81D0E56FB00B6AF29 /* UIApplication+TRReactiveBackgroundTask.m in Sources */, 4D76CCD41A99C3FA00DDE5EB /* TRRefreshLayer.m in Sources */, + 4ACA4F1B21DFD2ED00741046 /* AcknowledgementsViewController.swift in Sources */, 4DC716601A9B1D5B00BC4BD4 /* TRRefreshControl.m in Sources */, A10108FE1D336C6A0024B1BA /* WebViewController.swift in Sources */, 4A8163951CC830A700279952 /* TRWeatherUpdate+Analytics.m in Sources */, 51FEC99E21CD9F3300CDBC97 /* FadingImageView.swift in Sources */, + 4ACA4F3121E0166200741046 /* NilError.swift in Sources */, C2E44CFB1A3B76B3009CC844 /* NSNumber+TRRoundedNumber.m in Sources */, 51FEC99C21CD9E4A00CDBC97 /* CATransition.swift in Sources */, 4D7CDEBF1A46DD030038DD33 /* NSError+TRErrors.m in Sources */, @@ -1384,6 +1478,11 @@ target = 4A552EEE1CC7C8F8008C7298 /* TroposCore */; targetProxy = 4A552EFA1CC7C8F8008C7298 /* PBXContainerItemProxy */; }; + 4ACA4F2921E0022900741046 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4ACA4F1F21E0019400741046 /* Settings */; + targetProxy = 4ACA4F2821E0022900741046 /* PBXContainerItemProxy */; + }; 9BB8CE14790AABA30710CABC /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 45ADD324EABFD89F9E640007 /* Tropos */; @@ -1661,6 +1760,67 @@ }; name = Release; }; + 4ACA4F2521E0019400741046 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4ACA4F2621E0019400741046 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 4C523B4DE48AC8A7CC413F1E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1979,6 +2139,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 4ACA4F2421E0019400741046 /* Build configuration list for PBXNativeTarget "Settings" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4ACA4F2521E0019400741046 /* Debug */, + 4ACA4F2621E0019400741046 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; FA143D2AD1081C4274E37CCE /* Build configuration list for PBXNativeTarget "Tropos" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/bin/acknowledge/main.swift b/bin/acknowledge/main.swift new file mode 100644 index 0000000..190a301 --- /dev/null +++ b/bin/acknowledge/main.swift @@ -0,0 +1,121 @@ +import Foundation +import Settings + +enum AcknowledgeError: Error, LocalizedError { + case missingCarthageCheckouts + case settingsBundleInvalid(URL) + case settingsBundleNotFound + + var errorDescription: String? { + switch self { + case .missingCarthageCheckouts: + return "Unable to locate Carthage checkouts. Run 'carthage checkout' and try again." + case let .settingsBundleInvalid(url): + return "Invalid settings bundle at path: \(url.relativePath)" + case .settingsBundleNotFound: + return "Unable to locate Settings.bundle." + } + } +} + +extension FileManager.DirectoryEnumerator { + func nextFileURL(relativeTo url: URL) -> URL? { + return nextObject().map { path in + url.appendingPathComponent(path as! String) + } + } +} + +extension String { + func hasPrefix(_ prefix: String, options: CompareOptions) -> Bool { + return range(of: prefix, options: options.union(.anchored)) != nil + } +} + +func findSettingsBundle() throws -> URL { + let tropos = URL(fileURLWithPath: "Sources/Tropos") + let enumerator = FileManager.default.enumerator(atPath: tropos.path)! + + while let file = enumerator.nextFileURL(relativeTo: tropos) { + if file.lastPathComponent == "Settings.bundle" { + if Bundle(url: file) != nil { + return file + } else { + throw AcknowledgeError.settingsBundleInvalid(file) + } + } + } + + throw AcknowledgeError.settingsBundleNotFound +} + +func findLicenses(_ foundLicense: (_ library: String, _ license: URL) throws -> Void) throws { + let carthage = URL(fileURLWithPath: "Carthage/Checkouts") + + guard let enumerator = FileManager.default.enumerator(atPath: carthage.path) else { + throw AcknowledgeError.missingCarthageCheckouts + } + + var currentLibrary: String? + var currentLicense: URL? + + while let file = enumerator.nextFileURL(relativeTo: carthage) { + switch enumerator.level { + case 1: + if let currentLibrary = currentLibrary, let currentLicense = currentLicense { + try foundLicense(currentLibrary, currentLicense) + } + + currentLibrary = file.lastPathComponent + currentLicense = nil + + case 2: + if file.lastPathComponent.hasPrefix("license", options: .caseInsensitive) { + currentLicense = file + } + + default: + enumerator.skipDescendents() + } + } +} + +func main() throws { + let encoder = PropertyListEncoder() + encoder.outputFormat = .xml + let fileManager = FileManager.default + + let settings = try findSettingsBundle() + let acknowledgementsDir = settings.appendingPathComponent("Acknowledgements") + let acknowledgementsFile = acknowledgementsDir.appendingPathExtension("plist") + + try fileManager.removeItem(at: acknowledgementsDir) + try fileManager.createDirectory(at: acknowledgementsDir, withIntermediateDirectories: true) + + var acknowledgementsPage = PreferencePage() + + try findLicenses { library, license in + var childSpecifier = PreferenceSpecifier(type: "PSChildPaneSpecifier") + childSpecifier.file = "Acknowledgements/\(library)" + childSpecifier.title = library + acknowledgementsPage.preferenceSpecifiers.append(childSpecifier) + + var acknowledgements = PreferenceSpecifier(type: "PSGroupSpecifier") + acknowledgements.footerText = try String(contentsOf: license) + + let libraryPage = PreferencePage(preferenceSpecifiers: [acknowledgements]) + let libraryURL = acknowledgementsDir.appendingPathComponent("\(library).plist", isDirectory: false) + print("Writing \(libraryURL.relativePath)") + try fileManager.createFile(atPath: libraryURL.path, contents: encoder.encode(libraryPage)) + } + + print("Writing \(acknowledgementsFile.relativePath)") + try fileManager.createFile(atPath: acknowledgementsFile.path, contents: encoder.encode(acknowledgementsPage)) +} + +do { + try main() +} catch { + fputs("error: \(error.localizedDescription)\n", stderr) + exit(1) +} diff --git a/bin/generate-acknowledgements b/bin/generate-acknowledgements new file mode 100755 index 0000000..e91cf48 --- /dev/null +++ b/bin/generate-acknowledgements @@ -0,0 +1,3 @@ +#!/bin/sh + +exec swift run -c release acknowledge "$@" From 4d3ecb4f0ebea9b12dfb6f03b8c36db373c22a4c Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Jan 2019 12:36:20 -0500 Subject: [PATCH 03/10] Generate acknowledgements during deploy --- fastlane/Fastfile | 2 ++ fastlane/actions/generate_acknowledgements.rb | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 fastlane/actions/generate_acknowledgements.rb diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 9681772..89aca3a 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -19,6 +19,7 @@ platform :ios do setup_certs next_build_number(options) do + generate_acknowledgements build testflight(skip_waiting_for_build_processing: true) end @@ -29,6 +30,7 @@ platform :ios do setup_certs next_build_number(options) do + generate_acknowledgements build deliver(force: true) end diff --git a/fastlane/actions/generate_acknowledgements.rb b/fastlane/actions/generate_acknowledgements.rb new file mode 100644 index 0000000..c804e60 --- /dev/null +++ b/fastlane/actions/generate_acknowledgements.rb @@ -0,0 +1,25 @@ +module Fastlane + module Actions + class GenerateAcknowledgementsAction < Action + def self.run(params) + sh "bin/generate-acknowledgements" + end + + ##################################################### + # @!group Documentation + ##################################################### + + def self.description + "Generates acknowledgements in Settings.bundle" + end + + def self.authors + ["sharplet"] + end + + def self.is_supported?(platform) + platform == :ios + end + end + end +end From 42a4739d199577750250aef38bdd2a05c860068c Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 17:46:00 -0400 Subject: [PATCH 04/10] Improve error messages when generating acknowledgements --- bin/acknowledge/ConsoleError.swift | 67 ++++++++++++++++++++++++++ bin/acknowledge/StringExtensions.swift | 35 ++++++++++++++ bin/acknowledge/main.swift | 36 +++++++++----- 3 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 bin/acknowledge/ConsoleError.swift create mode 100644 bin/acknowledge/StringExtensions.swift diff --git a/bin/acknowledge/ConsoleError.swift b/bin/acknowledge/ConsoleError.swift new file mode 100644 index 0000000..befdea4 --- /dev/null +++ b/bin/acknowledge/ConsoleError.swift @@ -0,0 +1,67 @@ +import Foundation + +public protocol ConsoleError: LocalizedError, CustomNSError { + var filePath: String? { get } + var fileURL: URL? { get } +} + +extension Error { + public var consoleDescription: String { + let error = self as NSError + var message: String + + if var path = error.userInfo[NSFilePathErrorKey] as? String { + path.makeRelativePath() + message = "\(path): \(error.localizedDescription)" + } else { + message = error.localizedDescription + } + + if let failureReason = error.localizedFailureReason { + message += " (\(failureReason))" + } + + if let recoverySuggestion = error.localizedRecoverySuggestion { + message += ": \(recoverySuggestion)" + } + + message.applyTransform(.toLatinASCII) + return message + } +} + +extension ConsoleError { + public var filePath: String? { + return nil + } + + public var fileURL: String? { + return nil + } + + private var filePathFromURL: String? { + return fileURL?.relativePath + } + + public var errorUserInfo: [String: Any] { + var userInfo: [String: Any] = [:] + + if let errorDescription = errorDescription { + userInfo[NSLocalizedDescriptionKey] = errorDescription + } + + if let failureReason = failureReason { + userInfo[NSLocalizedFailureReasonErrorKey] = failureReason + } + + if let recoverySuggestion = recoverySuggestion { + userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion + } + + if let filePath = filePath ?? filePathFromURL { + userInfo[NSFilePathErrorKey] = filePath + } + + return userInfo + } +} diff --git a/bin/acknowledge/StringExtensions.swift b/bin/acknowledge/StringExtensions.swift new file mode 100644 index 0000000..0de90bc --- /dev/null +++ b/bin/acknowledge/StringExtensions.swift @@ -0,0 +1,35 @@ +import Foundation + +extension StringTransform { + public static let toLatinASCII = StringTransform("Latin-ASCII") +} + +extension String { + @discardableResult + public mutating func applyTransform(_ transform: StringTransform, reverse: Bool = false) -> Bool { + guard #available(macOS 10.11, *), + let transformed = applyingTransform(transform, reverse: reverse) + else { return false } + + self = transformed + return true + } + + public func hasPrefix(_ prefix: String, options: CompareOptions) -> Bool { + return range(of: prefix, options: options.union(.anchored)) != nil + } + + public mutating func makeRelativePath() { + let cwd = FileManager.default.currentDirectoryPath + + guard var range = range(of: cwd, options: .anchored) else { + return + } + + if range.upperBound < endIndex, self[range.upperBound] == "/" { + range = range.lowerBound ..< index(after: range.upperBound) + } + + removeSubrange(range) + } +} diff --git a/bin/acknowledge/main.swift b/bin/acknowledge/main.swift index 190a301..3f1f17c 100644 --- a/bin/acknowledge/main.swift +++ b/bin/acknowledge/main.swift @@ -1,7 +1,7 @@ import Foundation import Settings -enum AcknowledgeError: Error, LocalizedError { +enum AcknowledgeError: ConsoleError { case missingCarthageCheckouts case settingsBundleInvalid(URL) case settingsBundleNotFound @@ -9,13 +9,31 @@ enum AcknowledgeError: Error, LocalizedError { var errorDescription: String? { switch self { case .missingCarthageCheckouts: - return "Unable to locate Carthage checkouts. Run 'carthage checkout' and try again." - case let .settingsBundleInvalid(url): - return "Invalid settings bundle at path: \(url.relativePath)" + return "Unable to locate Carthage checkouts." + case .settingsBundleInvalid: + return "Invalid settings bundle." case .settingsBundleNotFound: return "Unable to locate Settings.bundle." } } + + var fileURL: URL? { + switch self { + case let .settingsBundleInvalid(url): + return url + case .missingCarthageCheckouts, .settingsBundleNotFound: + return nil + } + } + + var recoverySuggestion: String? { + switch self { + case .missingCarthageCheckouts: + return "Run 'carthage checkout' and try again." + case .settingsBundleInvalid, .settingsBundleNotFound: + return nil + } + } } extension FileManager.DirectoryEnumerator { @@ -26,12 +44,6 @@ extension FileManager.DirectoryEnumerator { } } -extension String { - func hasPrefix(_ prefix: String, options: CompareOptions) -> Bool { - return range(of: prefix, options: options.union(.anchored)) != nil - } -} - func findSettingsBundle() throws -> URL { let tropos = URL(fileURLWithPath: "Sources/Tropos") let enumerator = FileManager.default.enumerator(atPath: tropos.path)! @@ -116,6 +128,8 @@ func main() throws { do { try main() } catch { - fputs("error: \(error.localizedDescription)\n", stderr) + fputs("bin/generate-acknowledgements: ", stderr) + fputs(error.consoleDescription, stderr) + fputs("\n", stderr) exit(1) } From adf423b3c922a511e65ffcbbe16909348032e477 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 17:46:50 -0400 Subject: [PATCH 05/10] Don't fail if acknowledgements dir doesn't exist --- bin/acknowledge/main.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/acknowledge/main.swift b/bin/acknowledge/main.swift index 3f1f17c..f98542b 100644 --- a/bin/acknowledge/main.swift +++ b/bin/acknowledge/main.swift @@ -101,7 +101,12 @@ func main() throws { let acknowledgementsDir = settings.appendingPathComponent("Acknowledgements") let acknowledgementsFile = acknowledgementsDir.appendingPathExtension("plist") - try fileManager.removeItem(at: acknowledgementsDir) + do { + try fileManager.removeItem(at: acknowledgementsDir) + } catch CocoaError.fileNoSuchFile { + // no-op + } + try fileManager.createDirectory(at: acknowledgementsDir, withIntermediateDirectories: true) var acknowledgementsPage = PreferencePage() From 4ba586cd1c8a0ea890070983621ee122dbf46a8e Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 20:39:02 -0400 Subject: [PATCH 06/10] Move generate-acknowledgements to a sensible source location --- Package.swift | 2 +- .../generate-acknowledgements}/ConsoleError.swift | 0 .../generate-acknowledgements}/StringExtensions.swift | 0 .../generate-acknowledgements}/main.swift | 0 bin/generate-acknowledgements | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) rename {bin/acknowledge => Sources/generate-acknowledgements}/ConsoleError.swift (100%) rename {bin/acknowledge => Sources/generate-acknowledgements}/StringExtensions.swift (100%) rename {bin/acknowledge => Sources/generate-acknowledgements}/main.swift (100%) diff --git a/Package.swift b/Package.swift index 4a36158..0bf7833 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "Tropos", targets: [ - .target(name: "acknowledge", dependencies: ["Settings"], path: "bin/acknowledge"), + .target(name: "generate-acknowledgements", dependencies: ["Settings"]), .target(name: "Settings"), ] ) diff --git a/bin/acknowledge/ConsoleError.swift b/Sources/generate-acknowledgements/ConsoleError.swift similarity index 100% rename from bin/acknowledge/ConsoleError.swift rename to Sources/generate-acknowledgements/ConsoleError.swift diff --git a/bin/acknowledge/StringExtensions.swift b/Sources/generate-acknowledgements/StringExtensions.swift similarity index 100% rename from bin/acknowledge/StringExtensions.swift rename to Sources/generate-acknowledgements/StringExtensions.swift diff --git a/bin/acknowledge/main.swift b/Sources/generate-acknowledgements/main.swift similarity index 100% rename from bin/acknowledge/main.swift rename to Sources/generate-acknowledgements/main.swift diff --git a/bin/generate-acknowledgements b/bin/generate-acknowledgements index e91cf48..cf0015b 100755 --- a/bin/generate-acknowledgements +++ b/bin/generate-acknowledgements @@ -1,3 +1,3 @@ #!/bin/sh -exec swift run -c release acknowledge "$@" +exec swift run -c release generate-acknowledgements "$@" From 13b13f3a73fa99d4e2cd9e2160844911c57d690f Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 20:42:12 -0400 Subject: [PATCH 07/10] Don't bother with release configuration for genarate-acknowledgements --- bin/generate-acknowledgements | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/generate-acknowledgements b/bin/generate-acknowledgements index cf0015b..e412e5a 100755 --- a/bin/generate-acknowledgements +++ b/bin/generate-acknowledgements @@ -1,3 +1,3 @@ #!/bin/sh -exec swift run -c release generate-acknowledgements "$@" +exec swift run generate-acknowledgements "$@" From 0945952b53f3719e30ff39526a2023d6b3ebfb12 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 20:42:35 -0400 Subject: [PATCH 08/10] Generate acknowledgements in bin/setup --- bin/setup | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/setup b/bin/setup index a8643b1..e46d316 100755 --- a/bin/setup +++ b/bin/setup @@ -18,6 +18,7 @@ fi brew bundle gem_install xcpretty carthage bootstrap --platform iOS --cache-builds --no-use-binaries "${carthage_log_opts[@]}" +swift run generate-acknowledgements tropos_secrets="Config/Secrets.h" example_secrets="Config/Secrets-Example.h" From 88ea633e04d2a27a8de60491c95abe5e545f5a2b Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 15 Mar 2019 20:58:31 -0400 Subject: [PATCH 09/10] Write error messages using TextOutputStream --- Sources/generate-acknowledgements/FileStream.swift | 12 ++++++++++++ Sources/generate-acknowledgements/main.swift | 4 +--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Sources/generate-acknowledgements/FileStream.swift diff --git a/Sources/generate-acknowledgements/FileStream.swift b/Sources/generate-acknowledgements/FileStream.swift new file mode 100644 index 0000000..825d14f --- /dev/null +++ b/Sources/generate-acknowledgements/FileStream.swift @@ -0,0 +1,12 @@ +import Foundation + +public var standardOutput = FileStream(file: stdout) +public var standardError = FileStream(file: stderr) + +public struct FileStream: TextOutputStream { + var file: UnsafeMutablePointer + + public mutating func write(_ string: String) { + fputs(string, file) + } +} diff --git a/Sources/generate-acknowledgements/main.swift b/Sources/generate-acknowledgements/main.swift index f98542b..854bf50 100644 --- a/Sources/generate-acknowledgements/main.swift +++ b/Sources/generate-acknowledgements/main.swift @@ -133,8 +133,6 @@ func main() throws { do { try main() } catch { - fputs("bin/generate-acknowledgements: ", stderr) - fputs(error.consoleDescription, stderr) - fputs("\n", stderr) + print("bin/generate-acknowledgements: \(error.consoleDescription)", to: &standardError) exit(1) } From 70ca910736a254ec20590bad2d1a04335abe7c2a Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 21 Jun 2019 14:43:39 -0400 Subject: [PATCH 10/10] Build in Xcode 10.2, 11 / Swift 5+ --- .ruby-version | 1 - Cartfile | 2 +- Cartfile.resolved | 15 +++++++-------- .../Controllers/ForecastController.swift | 7 +++---- .../Controllers/GeocodeController.swift | 1 - .../Controllers/LocationController.swift | 9 ++++----- .../Extensions/ForecastController+Bridging.swift | 1 - .../TroposIntents/CheckWeatherIntentHandler.swift | 3 +-- Tests/TroposCoreTests/Support/TestGeocoder.swift | 1 - Tropos.xcodeproj/project.pbxproj | 14 +------------- 10 files changed, 17 insertions(+), 37 deletions(-) delete mode 100644 .ruby-version 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/Sources/TroposCore/Controllers/ForecastController.swift b/Sources/TroposCore/Controllers/ForecastController.swift index 8eb2b95..3850fa4 100644 --- a/Sources/TroposCore/Controllers/ForecastController.swift +++ b/Sources/TroposCore/Controllers/ForecastController.swift @@ -1,6 +1,5 @@ import CoreLocation import ReactiveSwift -import Result private var locationNotFound: Error { return NSError(domain: TRErrorDomain, code: TRError.conditionsResponseLocationNotFound.rawValue) @@ -33,8 +32,8 @@ public final class ForecastController: NSObject { urlSession = URLSession(configuration: configuration) } - public func fetchWeatherUpdate(for placemark: CLPlacemark) -> SignalProducer { - guard let location = placemark.location else { return SignalProducer(error: AnyError(locationNotFound)) } + public func fetchWeatherUpdate(for placemark: CLPlacemark) -> SignalProducer { + guard let location = placemark.location else { return SignalProducer(error: locationNotFound) } let today = conditionsRequest(for: location.coordinate, date: nil) let yesterday = conditionsRequest(for: location.coordinate, date: .yesterday) @@ -44,7 +43,7 @@ public final class ForecastController: NSObject { } } - private func fetch(_ conditionsRequest: URLRequest) -> SignalProducer<[String: Any], AnyError> { + private func fetch(_ conditionsRequest: URLRequest) -> SignalProducer<[String: Any], Error> { return urlSession.reactive.data(with: conditionsRequest).attemptMap { let (data, response) = $0 guard 200 ..< 300 ~= (response as! HTTPURLResponse).statusCode else { throw responseFailed } diff --git a/Sources/TroposCore/Controllers/GeocodeController.swift b/Sources/TroposCore/Controllers/GeocodeController.swift index eff4503..51aaa58 100644 --- a/Sources/TroposCore/Controllers/GeocodeController.swift +++ b/Sources/TroposCore/Controllers/GeocodeController.swift @@ -1,7 +1,6 @@ import CoreLocation import ReactiveObjCBridge import ReactiveSwift -import Result @objc(TRGeocodeController) public final class GeocodeController: NSObject { diff --git a/Sources/TroposCore/Controllers/LocationController.swift b/Sources/TroposCore/Controllers/LocationController.swift index 23e0441..ffe6209 100644 --- a/Sources/TroposCore/Controllers/LocationController.swift +++ b/Sources/TroposCore/Controllers/LocationController.swift @@ -2,15 +2,14 @@ import CoreLocation import Foundation import ReactiveObjCBridge import ReactiveSwift -import Result @objc(TRLocationController) open class LocationController: NSObject, CLLocationManagerDelegate { private let locationManager: CLLocationManager - private let locationUpdates = Signal.pipe() - private let locationUpdateError = Signal.pipe() + private let locationUpdates = Signal.pipe() + private let locationUpdateError = Signal.pipe() - private let statusChanged = Signal.pipe() + private let statusChanged = Signal.pipe() private let authorizationStatus: Property @objc public init(locationManager: CLLocationManager) { @@ -56,7 +55,7 @@ open class LocationController: NSObject, CLLocationManagerDelegate { } } - public func requestAuthorization() -> SignalProducer { + public func requestAuthorization() -> SignalProducer { return SignalProducer { [authorizationStatus, locationManager] observer, lifetime in let isAuthorized = authorizationStatus.producer.filterMap { status -> Bool? in switch status { diff --git a/Sources/TroposCore/Extensions/ForecastController+Bridging.swift b/Sources/TroposCore/Extensions/ForecastController+Bridging.swift index 11bcb59..b2ec288 100644 --- a/Sources/TroposCore/Extensions/ForecastController+Bridging.swift +++ b/Sources/TroposCore/Extensions/ForecastController+Bridging.swift @@ -1,6 +1,5 @@ import ReactiveObjCBridge import ReactiveSwift -import Result extension ForecastController { @objc(fetchWeatherUpdateForPlacemark:) diff --git a/Sources/TroposIntents/CheckWeatherIntentHandler.swift b/Sources/TroposIntents/CheckWeatherIntentHandler.swift index 3bb75c9..f8b791f 100644 --- a/Sources/TroposIntents/CheckWeatherIntentHandler.swift +++ b/Sources/TroposIntents/CheckWeatherIntentHandler.swift @@ -1,7 +1,6 @@ import Foundation import os.log import ReactiveSwift -import Result import TroposCore @available(iOS 12.0, *) @@ -20,7 +19,7 @@ public final class CheckWeatherIntentHandler: NSObject, CheckWeatherIntentHandli let weatherUpdate = location.requestAuthorization() .flatMap(.latest) { _ in location.requestLocation() } .flatMap(.latest) { location in geocode.reverseGeocode(location) } - .mapError(AnyError.init) + .mapError { $0 as Error } .flatMap(.latest) { placemark in forecast.fetchWeatherUpdate(for: placemark) } weatherUpdate.startWithResult { result in diff --git a/Tests/TroposCoreTests/Support/TestGeocoder.swift b/Tests/TroposCoreTests/Support/TestGeocoder.swift index 9c5b461..227e6b8 100644 --- a/Tests/TroposCoreTests/Support/TestGeocoder.swift +++ b/Tests/TroposCoreTests/Support/TestGeocoder.swift @@ -1,7 +1,6 @@ import CoreLocation import Foundation import MapKit -import Result @testable import TroposCore final class TestGeocoder: Geocoder { diff --git a/Tropos.xcodeproj/project.pbxproj b/Tropos.xcodeproj/project.pbxproj index c8abd33..fa2a4f8 100644 --- a/Tropos.xcodeproj/project.pbxproj +++ b/Tropos.xcodeproj/project.pbxproj @@ -37,7 +37,6 @@ 4A5FA30E212EFC6A00CC313C /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */; }; 4A5FA30F212EFC6A00CC313C /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */; }; 4A5FA310212EFC6A00CC313C /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C72040C32F00835531 /* ReactiveSwift.framework */; }; - 4A5FA311212EFC6A00CC313C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9CA2040C33000835531 /* Result.framework */; }; 4A5FA312212EFCD100CC313C /* CheckWeatherIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABCA2C420CAD14C00C46C4C /* CheckWeatherIntentHandler.swift */; }; 4A61FEAF1CF881A200B259E5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A552F801CC7D118008C7298 /* Localizable.strings */; }; 4A69873F1CC80414002D3F87 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A69873E1CC80414002D3F87 /* SettingsController.swift */; }; @@ -59,7 +58,6 @@ 4A6BB9CB2040C33000835531 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C72040C32F00835531 /* ReactiveSwift.framework */; }; 4A6BB9CC2040C33000835531 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */; }; 4A6BB9CD2040C33000835531 /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */; }; - 4A6BB9CE2040C33000835531 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9CA2040C33000835531 /* Result.framework */; }; 4A7A12351CC8219600363A5C /* TemperatureComparisonFormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7A12331CC8219600363A5C /* TemperatureComparisonFormatterSpec.swift */; }; 4A7A12361CC8219600363A5C /* TemperatureFormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7A12341CC8219600363A5C /* TemperatureFormatterSpec.swift */; }; 4A7A12391CC821DA00363A5C /* DailyForecastViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7A12381CC821DA00363A5C /* DailyForecastViewModelSpec.swift */; }; @@ -74,7 +72,6 @@ 4A92135921D568BD00F524AA /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0F135B1B432976001685BA /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4A92135A21D568BF00F524AA /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0F135A1B432976001685BA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4A92135B21D568C800F524AA /* ReactiveSwift.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C72040C32F00835531 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 4A92135C21D568CB00F524AA /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9CA2040C33000835531 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4A92135D21D568D700F524AA /* ReactiveObjCBridge.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4A92135E21D568D900F524AA /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4A92136021D5693C00F524AA /* ForecastControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A92135F21D5693C00F524AA /* ForecastControllerSpec.swift */; }; @@ -86,7 +83,6 @@ 4A95A142212DDFA600E0B33A /* Geocoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A23F4F92041127A00FDDC5E /* Geocoder.swift */; }; 4A95A145212DE1D300E0B33A /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */; }; 4A95A146212DE1D700E0B33A /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */; }; - 4A95A147212DE1DC00E0B33A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6BB9CA2040C33000835531 /* Result.framework */; }; 4A95A148212DE21000E0B33A /* GeocodeControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEF9E872040C47400CDDEEA /* GeocodeControllerSpec.swift */; }; 4A95A149212DE23800E0B33A /* TestGeocoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4B75E82040D0D600CE8E05 /* TestGeocoder.swift */; }; 4A95A14A212DE28D00E0B33A /* CoreLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4B75EA2040D10800CE8E05 /* CoreLocation.swift */; }; @@ -216,7 +212,6 @@ files = ( 4A92135E21D568D900F524AA /* ReactiveObjC.framework in Copy Frameworks */, 4A92135D21D568D700F524AA /* ReactiveObjCBridge.framework in Copy Frameworks */, - 4A92135C21D568CB00F524AA /* Result.framework in Copy Frameworks */, 4A92135921D568BD00F524AA /* Quick.framework in Copy Frameworks */, 4A92135B21D568C800F524AA /* ReactiveSwift.framework in Copy Frameworks */, 4A92135821D568B500F524AA /* OHHTTPStubs.framework in Copy Frameworks */, @@ -329,7 +324,6 @@ 4A6BB9C72040C32F00835531 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; }; 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = Carthage/Build/iOS/ReactiveObjC.framework; sourceTree = ""; }; 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjCBridge.framework; path = Carthage/Build/iOS/ReactiveObjCBridge.framework; sourceTree = ""; }; - 4A6BB9CA2040C33000835531 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; 4A7A12331CC8219600363A5C /* TemperatureComparisonFormatterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemperatureComparisonFormatterSpec.swift; sourceTree = ""; }; 4A7A12341CC8219600363A5C /* TemperatureFormatterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemperatureFormatterSpec.swift; sourceTree = ""; }; 4A7A12381CC821DA00363A5C /* DailyForecastViewModelSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailyForecastViewModelSpec.swift; sourceTree = ""; }; @@ -444,7 +438,6 @@ 4A5FA30E212EFC6A00CC313C /* ReactiveObjC.framework in Frameworks */, 4A5FA30F212EFC6A00CC313C /* ReactiveObjCBridge.framework in Frameworks */, 4A5FA310212EFC6A00CC313C /* ReactiveSwift.framework in Frameworks */, - 4A5FA311212EFC6A00CC313C /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -455,7 +448,6 @@ 4A95A151212DE48E00E0B33A /* Foundation.framework in Frameworks */, 4A95A146212DE1D700E0B33A /* ReactiveObjCBridge.framework in Frameworks */, 4A95A140212DDF5100E0B33A /* ReactiveSwift.framework in Frameworks */, - 4A95A147212DE1DC00E0B33A /* Result.framework in Frameworks */, 4A95A145212DE1D300E0B33A /* ReactiveObjC.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -487,7 +479,6 @@ 4AFCB1EA21D6C32100176FDD /* AppCenterCrashes.framework in Frameworks */, A1FC9ED21CFEA23B00E62618 /* TroposCore.framework in Frameworks */, 4A6BB9CC2040C33000835531 /* ReactiveObjC.framework in Frameworks */, - 4A6BB9CE2040C33000835531 /* Result.framework in Frameworks */, 4A6BB9CD2040C33000835531 /* ReactiveObjCBridge.framework in Frameworks */, 4AFCB1D721D6931400176FDD /* Mixpanel.framework in Frameworks */, 4A6BB9CB2040C33000835531 /* ReactiveSwift.framework in Frameworks */, @@ -897,7 +888,6 @@ 4A6BB9C82040C33000835531 /* ReactiveObjC.framework */, 4A6BB9C92040C33000835531 /* ReactiveObjCBridge.framework */, 4A6BB9C72040C32F00835531 /* ReactiveSwift.framework */, - 4A6BB9CA2040C33000835531 /* Result.framework */, ); name = Frameworks; sourceTree = ""; @@ -1178,7 +1168,7 @@ }; buildConfigurationList = 01015964F2486C2702150542 /* Build configuration list for PBXProject "Tropos" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -1260,7 +1250,6 @@ ); inputPaths = ( "$(SRCROOT)/Carthage/Build/iOS/ReactiveSwift.framework", - "$(SRCROOT)/Carthage/Build/iOS/Result.framework", "$(SRCROOT)/Carthage/Build/iOS/ReactiveObjC.framework", "$(SRCROOT)/Carthage/Build/iOS/ReactiveObjCBridge.framework", "$(SRCROOT)/Carthage/Build/iOS/Mixpanel.framework", @@ -1268,7 +1257,6 @@ name = "Copy Carthage Frameworks"; outputPaths = ( "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveSwift.framework", - "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework", "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveObjC.framework", "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveObjCBridge.framework", "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Mixpanel.framework",