From 6eda9456898deb79768ec88fd5ace460f7684c3d Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 25 Jul 2024 15:19:20 -0300 Subject: [PATCH] Restore `trunk` state as of `f85f732f9f6f10f0dcafc4918f94f4353f66c9a6` I accidentally pushed commits to `trunk` which should have been in a PR while experimenting with an updated code freeze workflow for version 4.52. This commit was done by comparing against the tip of `trunk` before my mistake, f85f732f9f6f10f0dcafc4918f94f4353f66c9a6 It reverts bfc2fc0c3ae11d892a624dc049862f1888a1285d and a774214e2eec8faaf26a4e367d7757d7616268c2, then cherry-picks all those changes there were somehow reverted in my previous fix attempt, eb52ef6d228f70ea5053d3139d93334f8fae709d --- Gemfile | 5 +- Gemfile.lock | 64 +++++++------ Simplenote.xcodeproj/project.pbxproj | 93 +++++++------------ .../xcshareddata/swiftpm/Package.resolved | 12 ++- Simplenote/AccountRemote.swift | 54 ----------- .../Classes/MagicLinkAuthenticator.swift | 1 + Simplenote/Classes/RemoteConstants.swift | 12 --- Simplenote/Classes/SPAuthError.swift | 36 ++++++- Simplenote/Classes/SPAuthHandler.swift | 37 +++----- Simplenote/Classes/SPAuthViewController.swift | 71 +++++++++++--- .../SPSettingsViewController+Extensions.swift | 9 +- Simplenote/Classes/SignupRemote.swift | 24 ----- .../AccountDeletionController.swift | 1 + Simplenote/LoginRemote.swift | 46 --------- Simplenote/Remote.swift | 73 --------------- Simplenote/RemoteError.swift | 38 -------- Simplenote/SPUser+UserProtocol.swift | 8 ++ Simplenote/UIAlertController+Auth.swift | 22 +++++ Simplenote/URLRequest+Simplenote.swift | 13 --- .../AccountVerificationController.swift | 1 + SimplenoteTests/Network/MockURLSession.swift | 15 --- .../Network/MockURLSessionDataTask.swift | 15 --- .../RemoteResult+TestHelpers.swift | 3 +- SimplenoteTests/SignupRemoteTests.swift | 75 --------------- .../AccountVerificationControllerTests.swift | 1 + .../AccountVerificationRemoteTests.swift | 51 ---------- .../MockAccountVerificationRemote.swift | 1 + 27 files changed, 222 insertions(+), 559 deletions(-) delete mode 100644 Simplenote/AccountRemote.swift delete mode 100644 Simplenote/Classes/RemoteConstants.swift delete mode 100644 Simplenote/Classes/SignupRemote.swift delete mode 100644 Simplenote/LoginRemote.swift delete mode 100644 Simplenote/Remote.swift delete mode 100644 Simplenote/RemoteError.swift create mode 100644 Simplenote/SPUser+UserProtocol.swift create mode 100644 Simplenote/UIAlertController+Auth.swift delete mode 100644 Simplenote/URLRequest+Simplenote.swift delete mode 100644 SimplenoteTests/Network/MockURLSession.swift delete mode 100644 SimplenoteTests/Network/MockURLSessionDataTask.swift delete mode 100644 SimplenoteTests/SignupRemoteTests.swift delete mode 100644 SimplenoteTests/Verification/AccountVerificationRemoteTests.swift diff --git a/Gemfile b/Gemfile index 085001d07..a2653bc3a 100644 --- a/Gemfile +++ b/Gemfile @@ -8,8 +8,11 @@ gem 'danger-dangermattic', '~> 1.0' gem 'fastlane', '~> 2' gem 'fastlane-plugin-appcenter', '~> 2.1.2' gem 'fastlane-plugin-sentry', '~> 1.6' -gem 'fastlane-plugin-wpmreleasetoolkit', '~> 11.0', '>= 11.0.1' +gem 'fastlane-plugin-wpmreleasetoolkit', '~> 11.0.1' group :screenshots, optional: true do gem 'rmagick', '~> 3.2.0' end + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 5e891f661..5f2447ee7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,7 +5,7 @@ GEM base64 nkf rexml - activesupport (7.1.3.4) + activesupport (7.1.3.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -15,8 +15,8 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) @@ -24,24 +24,24 @@ GEM ast (2.4.2) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.957.0) - aws-sdk-core (3.201.2) + aws-partitions (1.921.0) + aws-sdk-core (3.193.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.80.0) + aws-sdk-core (~> 3, >= 3.193.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.148.0) + aws-sdk-core (~> 3, >= 3.193.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) - bigdecimal (3.1.8) + bigdecimal (3.1.7) buildkit (1.6.0) sawyer (>= 0.6) chroma (0.2.0) @@ -91,7 +91,7 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.2.3) connection_pool (2.4.1) cork (0.3.0) colored2 (~> 3.1) @@ -129,7 +129,7 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.111.0) + excon (0.110.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -161,7 +161,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.221.1) + fastlane (2.220.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -205,7 +205,7 @@ GEM fastlane-plugin-appcenter (2.1.2) fastlane-plugin-sentry (1.22.0) os (~> 1.1, >= 1.1.4) - fastlane-plugin-wpmreleasetoolkit (11.0.3) + fastlane-plugin-wpmreleasetoolkit (11.0.1) activesupport (>= 6.1.7.1) buildkit (~> 1.5) chroma (= 0.2.0) @@ -266,27 +266,27 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.6) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.14.5) + i18n (1.14.4) concurrent-ruby (~> 1.0) java-properties (0.3.0) jmespath (1.6.2) json (2.7.2) - jwt (2.8.2) + jwt (2.8.1) base64 kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) language_server-protocol (3.17.0.3) - mini_magick (4.13.2) + mini_magick (4.12.0) mini_mime (1.1.5) - minitest (5.24.1) + minitest (5.22.3) molinillo (0.8.0) multi_json (1.15.0) - multipart-post (2.4.1) + multipart-post (2.4.0) mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) @@ -294,9 +294,9 @@ GEM netrc (0.11.0) nkf (0.2.0) no_proxy_fix (0.1.2) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.16.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.4-x86_64-linux) racc (~> 1.4) octokit (6.1.1) faraday (>= 1, < 3) @@ -305,16 +305,16 @@ GEM options (2.3.2) optparse (0.5.0) os (1.1.4) - parallel (1.25.1) + parallel (1.24.0) parser (3.3.1.0) ast (~> 2.4.1) racc plist (3.7.1) - progress_bar (1.3.4) - highline (>= 1.6) + progress_bar (1.3.3) + highline (>= 1.6, < 3) options (~> 2.3.0) public_suffix (4.0.7) - racc (1.8.0) + racc (1.7.3) rainbow (3.1.1) rake (13.2.1) rake-compiler (1.2.7) @@ -326,8 +326,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.9) - strscan + rexml (3.2.6) rmagick (3.2.0) rouge (2.0.7) rubocop (1.63.4) @@ -359,7 +358,6 @@ GEM simctl (1.6.10) CFPropertyList naturally - strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -399,8 +397,8 @@ DEPENDENCIES fastlane (~> 2) fastlane-plugin-appcenter (~> 2.1.2) fastlane-plugin-sentry (~> 1.6) - fastlane-plugin-wpmreleasetoolkit (~> 11.0, >= 11.0.1) + fastlane-plugin-wpmreleasetoolkit (~> 11.0.1) rmagick (~> 3.2.0) BUNDLED WITH - 2.4.21 + 2.5.4 diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index 6a0958985..6154f0d96 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -124,7 +124,6 @@ A61471B925D70C190065D849 /* PopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61471B725D70C190065D849 /* PopoverViewController.swift */; }; A628BEB625ECD97900121B64 /* SignupVerificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A628BEB425ECD97900121B64 /* SignupVerificationViewController.swift */; }; A628BEB725ECD97900121B64 /* SignupVerificationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A628BEB525ECD97900121B64 /* SignupVerificationViewController.xib */; }; - A628BEC325ED703A00121B64 /* RemoteConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A628BEC225ED703A00121B64 /* RemoteConstants.swift */; }; A6344AD9255952C70072FA07 /* NoteContentPreviewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6344AD8255952C70072FA07 /* NoteContentPreviewTests.swift */; }; A645FA70254C5E69008A1519 /* NoteContentHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A645FA6F254C5E69008A1519 /* NoteContentHelperTests.swift */; }; A64DE6E9255D1C9F001D0526 /* ContentSlice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64DE6E8255D1C9F001D0526 /* ContentSlice.swift */; }; @@ -172,13 +171,8 @@ A6C0DFE825C18E3300B9BE39 /* NoteEditorTagListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A6C0DFE625C18E3300B9BE39 /* NoteEditorTagListViewController.xib */; }; A6C2721825AF0C1E00593731 /* TagListViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6C2721725AF0C1E00593731 /* TagListViewCell.swift */; }; A6C3D8B7256687970042F584 /* SPObjectManager+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6C3D8B6256687970042F584 /* SPObjectManager+Simplenote.swift */; }; - A6C7648025E9131C00A39067 /* SignupRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6C7647F25E9131C00A39067 /* SignupRemote.swift */; }; - A6CC0B0725B8287F00F12A85 /* AccountRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B0625B8287F00F12A85 /* AccountRemote.swift */; }; A6CC0B1025B83FE400F12A85 /* AccountVerificationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B0F25B83FE400F12A85 /* AccountVerificationControllerTests.swift */; }; A6CC0B1F25B840A400F12A85 /* MockAccountVerificationRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B1E25B840A400F12A85 /* MockAccountVerificationRemote.swift */; }; - A6CC0B2D25B84FF200F12A85 /* AccountVerificationRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B2C25B84FF200F12A85 /* AccountVerificationRemoteTests.swift */; }; - A6CC0B3625B8502800F12A85 /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B3525B8502800F12A85 /* MockURLSession.swift */; }; - A6CC0B4425B8505700F12A85 /* MockURLSessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CC0B4325B8505700F12A85 /* MockURLSessionDataTask.swift */; }; A6CDD9C825F0163D00E0BC4D /* MagicLinkAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CDD9C725F0163D00E0BC4D /* MagicLinkAuthenticator.swift */; }; A6CDF900256B9CB900CF2F27 /* ViewSpinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6CDF8FF256B9CB900CF2F27 /* ViewSpinner.swift */; }; A6D5AE6525483F8A00326C76 /* NSTextStorage+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D5AE6425483F8A00326C76 /* NSTextStorage+Simplenote.swift */; }; @@ -241,6 +235,8 @@ B518899E1E0D5EF800E71B83 /* SPContactsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B518899D1E0D5EF800E71B83 /* SPContactsManager.swift */; }; B51889A01E0D5F2200E71B83 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B518899F1E0D5F2200E71B83 /* Contacts.framework */; }; B51889A41E0DB01E00E71B83 /* ContactsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B51889A31E0DB01E00E71B83 /* ContactsUI.framework */; }; + B51D445B2C52CB4000F296A7 /* SimplenoteEndpoints in Frameworks */ = {isa = PBXBuildFile; productRef = B51D445A2C52CB4000F296A7 /* SimplenoteEndpoints */; }; + B51D445E2C52CDA800F296A7 /* SPUser+UserProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51D445D2C52CDA800F296A7 /* SPUser+UserProtocol.swift */; }; B51F6DD12460C3EE0074DDD9 /* AuthenticationValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F6DD02460C3EE0074DDD9 /* AuthenticationValidator.swift */; }; B51F6DD32460D12B0074DDD9 /* AuthenticationValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F6DD22460D12B0074DDD9 /* AuthenticationValidatorTests.swift */; }; B51F6DD52460D7540074DDD9 /* NSPredicate+Email.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F6DD42460D7540074DDD9 /* NSPredicate+Email.swift */; }; @@ -298,6 +294,7 @@ B54A11C22C135EA2002AC8AA /* UIButton+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A11C12C135EA2002AC8AA /* UIButton+Simplenote.swift */; }; B54A11C42C136225002AC8AA /* MagicLinkRequestedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A11C32C136225002AC8AA /* MagicLinkRequestedView.swift */; }; B54B04D82407169500401FBB /* SPAppDelegate+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54B04D72407169500401FBB /* SPAppDelegate+Extensions.swift */; }; + B54C1B552C4EE350001E9E18 /* UIAlertController+Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54C1B542C4EE350001E9E18 /* UIAlertController+Auth.swift */; }; B54D9C572909BA2600D0E0EC /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D9C562909BA2600D0E0EC /* StoreManager.swift */; }; B54D9C592909CC4400D0E0EC /* StoreProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D9C582909CC4400D0E0EC /* StoreProduct.swift */; }; B55051821A4328B9002A1093 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B55051811A4328B9002A1093 /* LocalAuthentication.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -310,7 +307,6 @@ B55E428C22A1A4550018C0CE /* SPSortOrderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55E428B22A1A4550018C0CE /* SPSortOrderViewController.swift */; }; B56315BE236BD9970066C151 /* UIKitConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AB169B22FB2DF300B4EBA5 /* UIKitConstants.swift */; }; B5686178195B9E93005F1245 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2F149471799F5A500DC9690 /* libsqlite3.dylib */; }; - B5699B092C1246C40096D6B7 /* LoginRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5699B082C1246C40096D6B7 /* LoginRemote.swift */; }; B56A695822F9CD1500B90398 /* SPOnboardingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B56A695622F9CD1500B90398 /* SPOnboardingViewController.xib */; }; B56A695922F9CD1500B90398 /* SPOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56A695722F9CD1500B90398 /* SPOnboardingViewController.swift */; }; B56A695B22F9CD4E00B90398 /* UINavigationBar+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56A695A22F9CD4E00B90398 /* UINavigationBar+Simplenote.swift */; }; @@ -428,7 +424,6 @@ BA0AF10D2BE996600050EEBD /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59560DF251A46D500A06788 /* KeychainManager.swift */; }; BA0AF10E2BE996630050EEBD /* KeychainPasswordItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD30471FC4CFA2008D0B78 /* KeychainPasswordItem.swift */; }; BA0ED16E26D708AC002533B6 /* Color+Widgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0ED16D26D708AC002533B6 /* Color+Widgets.swift */; }; - BA0F5E0426B62A8B0098C605 /* RemoteError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0F5E0326B62A8B0098C605 /* RemoteError.swift */; }; BA113E4D269E860500F3E3B4 /* markdown-light.css in Resources */ = {isa = PBXBuildFile; fileRef = BA113E4C269E860500F3E3B4 /* markdown-light.css */; }; BA122DBA265EF402003D3BC5 /* SimplenoteConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5095DC824632E3300812711 /* SimplenoteConstants.swift */; }; BA122DBB265EF40E003D3BC5 /* UIColor+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B575736B232D454300443C2E /* UIColor+Helpers.swift */; }; @@ -441,7 +436,6 @@ BA122DCA265F2E2D003D3BC5 /* UIColorSimplenoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA122DC9265F2E2D003D3BC5 /* UIColorSimplenoteTests.swift */; }; BA12B06F26B0D0150026F31D /* SPManagedObject+Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA12B06C26B0D0150026F31D /* SPManagedObject+Widget.swift */; }; BA12B07026B0D0150026F31D /* SPManagedObject+Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA12B06C26B0D0150026F31D /* SPManagedObject+Widget.swift */; }; - BA18532826488DBC00D9A347 /* SignupRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA18532726488DBC00D9A347 /* SignupRemoteTests.swift */; }; BA2015BB2B57384F005E59AA /* AutomatticTracks in Frameworks */ = {isa = PBXBuildFile; productRef = BA2015BA2B57384F005E59AA /* AutomatticTracks */; }; BA289B5C2BE4371A000E6794 /* ListWidgetIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA289B5A2BE4371A000E6794 /* ListWidgetIntentHandler.swift */; }; BA289B5F2BE43728000E6794 /* NoteWidgetIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA289B5D2BE43728000E6794 /* NoteWidgetIntentHandler.swift */; }; @@ -476,10 +470,8 @@ BA55124E2600210B00D8F882 /* TimerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA55124D2600210B00D8F882 /* TimerFactory.swift */; }; BA55B05A25F067DF0042582B /* NoticePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA55B05925F067DF0042582B /* NoticePresenter.swift */; }; BA55B06325F068650042582B /* NoticeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA55B06225F068650042582B /* NoticeController.swift */; }; - BA5768E3269A803F008B510E /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5768E2269A803F008B510E /* Remote.swift */; }; BA5768EC269BE4D0008B510E /* AccountDeletionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5768EB269BE4D0008B510E /* AccountDeletionController.swift */; }; BA5C1C0725BF9D6C006E3820 /* SPDragBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5C1C0625BF9D6C006E3820 /* SPDragBar.swift */; }; - BA5E5B7D264A148C00D0EE19 /* URLRequest+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5E5B7C264A148C00D0EE19 /* URLRequest+Simplenote.swift */; }; BA608EF526BB6E0200A9D94E /* ListWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA608EF426BB6E0200A9D94E /* ListWidget.swift */; }; BA608EF726BB6E7400A9D94E /* ListWidgetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA608EF626BB6E7400A9D94E /* ListWidgetProvider.swift */; }; BA608EF926BB6F4C00A9D94E /* ListWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA608EF826BB6F4C00A9D94E /* ListWidgetView.swift */; }; @@ -828,7 +820,6 @@ A61471B725D70C190065D849 /* PopoverViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PopoverViewController.swift; path = Classes/PopoverViewController.swift; sourceTree = ""; }; A628BEB425ECD97900121B64 /* SignupVerificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SignupVerificationViewController.swift; path = Classes/SignupVerificationViewController.swift; sourceTree = ""; }; A628BEB525ECD97900121B64 /* SignupVerificationViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = SignupVerificationViewController.xib; path = Classes/SignupVerificationViewController.xib; sourceTree = ""; }; - A628BEC225ED703A00121B64 /* RemoteConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RemoteConstants.swift; path = Classes/RemoteConstants.swift; sourceTree = ""; }; A6344AD8255952C70072FA07 /* NoteContentPreviewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContentPreviewTests.swift; sourceTree = ""; }; A645FA6F254C5E69008A1519 /* NoteContentHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContentHelperTests.swift; sourceTree = ""; }; A64DE6E8255D1C9F001D0526 /* ContentSlice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContentSlice.swift; path = Classes/ContentSlice.swift; sourceTree = ""; }; @@ -876,13 +867,8 @@ A6C0DFE625C18E3300B9BE39 /* NoteEditorTagListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NoteEditorTagListViewController.xib; sourceTree = ""; }; A6C2721725AF0C1E00593731 /* TagListViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagListViewCell.swift; sourceTree = ""; }; A6C3D8B6256687970042F584 /* SPObjectManager+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SPObjectManager+Simplenote.swift"; path = "Classes/SPObjectManager+Simplenote.swift"; sourceTree = ""; }; - A6C7647F25E9131C00A39067 /* SignupRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SignupRemote.swift; path = Classes/SignupRemote.swift; sourceTree = ""; }; - A6CC0B0625B8287F00F12A85 /* AccountRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRemote.swift; sourceTree = ""; }; A6CC0B0F25B83FE400F12A85 /* AccountVerificationControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountVerificationControllerTests.swift; sourceTree = ""; }; A6CC0B1E25B840A400F12A85 /* MockAccountVerificationRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAccountVerificationRemote.swift; sourceTree = ""; }; - A6CC0B2C25B84FF200F12A85 /* AccountVerificationRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountVerificationRemoteTests.swift; sourceTree = ""; }; - A6CC0B3525B8502800F12A85 /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = ""; }; - A6CC0B4325B8505700F12A85 /* MockURLSessionDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSessionDataTask.swift; sourceTree = ""; }; A6CDD9C725F0163D00E0BC4D /* MagicLinkAuthenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MagicLinkAuthenticator.swift; path = Classes/MagicLinkAuthenticator.swift; sourceTree = ""; }; A6CDF8FF256B9CB900CF2F27 /* ViewSpinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewSpinner.swift; path = Classes/ViewSpinner.swift; sourceTree = ""; }; A6D5AE6425483F8A00326C76 /* NSTextStorage+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextStorage+Simplenote.swift"; sourceTree = ""; }; @@ -943,6 +929,7 @@ B518899D1E0D5EF800E71B83 /* SPContactsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SPContactsManager.swift; path = Classes/SPContactsManager.swift; sourceTree = ""; }; B518899F1E0D5F2200E71B83 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; }; B51889A31E0DB01E00E71B83 /* ContactsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ContactsUI.framework; path = System/Library/Frameworks/ContactsUI.framework; sourceTree = SDKROOT; }; + B51D445D2C52CDA800F296A7 /* SPUser+UserProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SPUser+UserProtocol.swift"; sourceTree = ""; }; B51F6DD02460C3EE0074DDD9 /* AuthenticationValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationValidator.swift; sourceTree = ""; }; B51F6DD22460D12B0074DDD9 /* AuthenticationValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationValidatorTests.swift; sourceTree = ""; }; B51F6DD42460D7540074DDD9 /* NSPredicate+Email.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPredicate+Email.swift"; sourceTree = ""; }; @@ -998,6 +985,7 @@ B54A11C12C135EA2002AC8AA /* UIButton+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Simplenote.swift"; sourceTree = ""; }; B54A11C32C136225002AC8AA /* MagicLinkRequestedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicLinkRequestedView.swift; sourceTree = ""; }; B54B04D72407169500401FBB /* SPAppDelegate+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SPAppDelegate+Extensions.swift"; sourceTree = ""; }; + B54C1B542C4EE350001E9E18 /* UIAlertController+Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Auth.swift"; sourceTree = ""; }; B54D9C542909B21700D0E0EC /* Simplenote 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Simplenote 6.xcdatamodel"; sourceTree = ""; }; B54D9C562909BA2600D0E0EC /* StoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = ""; }; B54D9C582909CC4400D0E0EC /* StoreProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreProduct.swift; sourceTree = ""; }; @@ -1022,7 +1010,6 @@ B55E428B22A1A4550018C0CE /* SPSortOrderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SPSortOrderViewController.swift; path = Classes/SPSortOrderViewController.swift; sourceTree = ""; }; B5651239243BC063001EFE77 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = ""; }; B566A1DD189AA74600DC3F2D /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; - B5699B082C1246C40096D6B7 /* LoginRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginRemote.swift; sourceTree = ""; }; B56A695622F9CD1500B90398 /* SPOnboardingViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SPOnboardingViewController.xib; path = Classes/SPOnboardingViewController.xib; sourceTree = ""; }; B56A695722F9CD1500B90398 /* SPOnboardingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SPOnboardingViewController.swift; path = Classes/SPOnboardingViewController.swift; sourceTree = ""; }; B56A695A22F9CD4E00B90398 /* UINavigationBar+Simplenote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UINavigationBar+Simplenote.swift"; path = "Classes/UINavigationBar+Simplenote.swift"; sourceTree = ""; }; @@ -1154,13 +1141,11 @@ BA0890A626BB9BE20035CA48 /* ListWidgetHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetHeaderView.swift; sourceTree = ""; }; BA0890A826BB9BF80035CA48 /* NewNoteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewNoteButton.swift; sourceTree = ""; }; BA0ED16D26D708AC002533B6 /* Color+Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Widgets.swift"; sourceTree = ""; }; - BA0F5E0326B62A8B0098C605 /* RemoteError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteError.swift; sourceTree = ""; }; BA113E4C269E860500F3E3B4 /* markdown-light.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = "markdown-light.css"; sourceTree = ""; }; BA122DC5265EF90C003D3BC5 /* NewNoteWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewNoteWidgetView.swift; sourceTree = ""; }; BA122DC9265F2E2D003D3BC5 /* UIColorSimplenoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorSimplenoteTests.swift; sourceTree = ""; }; BA12B06C26B0D0150026F31D /* SPManagedObject+Widget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SPManagedObject+Widget.swift"; sourceTree = ""; }; BA16C6A82BC4968400C9079F /* Simplenote 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Simplenote 7.xcdatamodel"; sourceTree = ""; }; - BA18532726488DBC00D9A347 /* SignupRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupRemoteTests.swift; sourceTree = ""; }; BA289B5A2BE4371A000E6794 /* ListWidgetIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetIntentHandler.swift; sourceTree = ""; }; BA289B5D2BE43728000E6794 /* NoteWidgetIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteWidgetIntentHandler.swift; sourceTree = ""; }; BA289B632BE43963000E6794 /* OpenNewNoteIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenNewNoteIntentHandler.swift; sourceTree = ""; }; @@ -1185,10 +1170,8 @@ BA55124D2600210B00D8F882 /* TimerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerFactory.swift; sourceTree = ""; }; BA55B05925F067DF0042582B /* NoticePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticePresenter.swift; sourceTree = ""; }; BA55B06225F068650042582B /* NoticeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeController.swift; sourceTree = ""; }; - BA5768E2269A803F008B510E /* Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Remote.swift; sourceTree = ""; }; BA5768EB269BE4D0008B510E /* AccountDeletionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionController.swift; sourceTree = ""; }; BA5C1C0625BF9D6C006E3820 /* SPDragBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPDragBar.swift; sourceTree = ""; }; - BA5E5B7C264A148C00D0EE19 /* URLRequest+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Simplenote.swift"; sourceTree = ""; }; BA608EF426BB6E0200A9D94E /* ListWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidget.swift; sourceTree = ""; }; BA608EF626BB6E7400A9D94E /* ListWidgetProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetProvider.swift; sourceTree = ""; }; BA608EF826BB6F4C00A9D94E /* ListWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetView.swift; sourceTree = ""; }; @@ -1415,6 +1398,7 @@ 46A3C9BE17DFA81A002865AE /* UIKit.framework in Frameworks */, 46A3C9BF17DFA81A002865AE /* Foundation.framework in Frameworks */, 4352BA0867E0E416EB5FF6B9 /* Pods_Automattic_Simplenote.framework in Frameworks */, + B51D445B2C52CB4000F296A7 /* SimplenoteEndpoints in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1734,7 +1718,6 @@ children = ( A6CC0B1D25B8408900F12A85 /* Helpers */, A6CC0B0F25B83FE400F12A85 /* AccountVerificationControllerTests.swift */, - A6CC0B2C25B84FF200F12A85 /* AccountVerificationRemoteTests.swift */, ); path = Verification; sourceTree = ""; @@ -1747,15 +1730,6 @@ path = Helpers; sourceTree = ""; }; - A6CC0B3425B8501B00F12A85 /* Network */ = { - isa = PBXGroup; - children = ( - A6CC0B3525B8502800F12A85 /* MockURLSession.swift */, - A6CC0B4325B8505700F12A85 /* MockURLSessionDataTask.swift */, - ); - path = Network; - sourceTree = ""; - }; A6E1E79C24BDDF30008A44BC /* Card */ = { isa = PBXGroup; children = ( @@ -1841,11 +1815,18 @@ B52F35D022F3254800724793 /* Theme.swift */, B5AB169B22FB2DF300B4EBA5 /* UIKitConstants.swift */, B5095DC824632E3300812711 /* SimplenoteConstants.swift */, - A628BEC225ED703A00121B64 /* RemoteConstants.swift */, ); name = Settings; sourceTree = ""; }; + B51D445C2C52CD8D00F296A7 /* Authentication */ = { + isa = PBXGroup; + children = ( + B51D445D2C52CDA800F296A7 /* SPUser+UserProtocol.swift */, + ); + name = Authentication; + sourceTree = ""; + }; B51F6DCF2460C3D80074DDD9 /* Onboarding */ = { isa = PBXGroup; children = ( @@ -1995,7 +1976,6 @@ A6344AD8255952C70072FA07 /* NoteContentPreviewTests.swift */, A694ABB225D1687200CC3A2D /* NoteScrollPositionCacheTests.swift */, BAB0179A260AD591007A9CC3 /* PublishControllerTests.swift */, - BA18532726488DBC00D9A347 /* SignupRemoteTests.swift */, ); name = Tools; sourceTree = ""; @@ -2063,7 +2043,6 @@ A6C0DFB425C1581D00B9BE39 /* UIScrollView+Simplenote.swift */, A6A8968D25D5779D00A7B390 /* FileManager+Simplenote.swift */, BAE08625261282D1009D40CD /* Note+Publish.swift */, - BA5E5B7C264A148C00D0EE19 /* URLRequest+Simplenote.swift */, BAFDBBD726B88BD200119615 /* Date+Simplenote.swift */, BAB27BD526BA425C00AE4ACC /* Color+Simplenote.swift */, BAA59E78269F9FE30068BD3D /* Date+Simplenote.swift */, @@ -2081,6 +2060,7 @@ B56A695D22F9D53300B90398 /* SPAuthError.swift */, B56A695C22F9D53300B90398 /* SPAuthHandler.swift */, B5C99A502C45B94600728813 /* AuthenticationMode.swift */, + B54C1B542C4EE350001E9E18 /* UIAlertController+Auth.swift */, B56A695F22F9D53400B90398 /* SPAuthViewController.swift */, B56A695E22F9D53300B90398 /* SPAuthViewController.xib */, B5AB169722FA124F00B4EBA5 /* SPSheetController.swift */, @@ -2180,7 +2160,6 @@ B5B85BA6235173D900AD5221 /* Helpers */ = { isa = PBXGroup; children = ( - A6CC0B3425B8501B00F12A85 /* Network */, B5421F3B23CE7A14004DDC19 /* Constants.swift */, B5421F3D23CE7A1C004DDC19 /* MockupStorage.swift */, B5421F3E23CE7A1C004DDC19 /* MockupStorage+Sample.swift */, @@ -2216,7 +2195,6 @@ B5BE053E1AB751FD002417BF /* Tools */ = { isa = PBXGroup; children = ( - BA57692A269D2103008B510E /* Remotes */, B51F6DCF2460C3D80074DDD9 /* Onboarding */, B5CBEF3F22D3AD92009DBE67 /* MigrationsHandler.swift */, B5D3FCCF201F96AC00A813B7 /* StatusChecker.h */, @@ -2437,18 +2415,6 @@ path = Tools; sourceTree = ""; }; - BA57692A269D2103008B510E /* Remotes */ = { - isa = PBXGroup; - children = ( - B5699B082C1246C40096D6B7 /* LoginRemote.swift */, - A6C7647F25E9131C00A39067 /* SignupRemote.swift */, - A6CC0B0625B8287F00F12A85 /* AccountRemote.swift */, - BA5768E2269A803F008B510E /* Remote.swift */, - BA0F5E0326B62A8B0098C605 /* RemoteError.swift */, - ); - name = Remotes; - sourceTree = ""; - }; BA75D87C26C0842000883FFA /* Extensions */ = { isa = PBXGroup; children = ( @@ -2700,6 +2666,7 @@ E29ADD4117848E8500E55842 /* Simplenote */ = { isa = PBXGroup; children = ( + B51D445C2C52CD8D00F296A7 /* Authentication */, 467D9C7A1788D10400785EF3 /* Categories */, A690032F253D85C40087D0D2 /* Controllers */, B5C7BF92230C592B000DEC91 /* Credentials */, @@ -2913,6 +2880,7 @@ B50D2FB524E6DFAC00163FC3 /* SimplenoteSearch */, B59CACFB2541E05400958330 /* SimplenoteInterlinks */, BA2015BA2B57384F005E59AA /* AutomatticTracks */, + B51D445A2C52CB4000F296A7 /* SimplenoteEndpoints */, ); productName = Simplenote; productReference = 46A3C9D717DFA81A002865AE /* Simplenote.app */; @@ -3132,6 +3100,7 @@ 3F762E8D2677F19C0088CD45 /* XCRemoteSwiftPackageReference "XCUITestHelpers" */, 3F4BA07C26CDF295000619B1 /* XCRemoteSwiftPackageReference "ScreenObject" */, BA2015B92B573761005E59AA /* XCRemoteSwiftPackageReference "Automattic-Tracks-iOS" */, + B51D44592C52CB4000F296A7 /* XCRemoteSwiftPackageReference "SimplenoteEndpoints-Swift" */, ); productRefGroup = E29ADD3917848E8500E55842 /* Products */; projectDirPath = ""; @@ -3566,7 +3535,6 @@ B56E763622BD565700C5AA47 /* UIView+Simplenote.swift in Sources */, B5E4082D235DE41A00D4E1DF /* UIColor+Studio.swift in Sources */, A6CDF900256B9CB900CF2F27 /* ViewSpinner.swift in Sources */, - BA5E5B7D264A148C00D0EE19 /* URLRequest+Simplenote.swift in Sources */, 375D24B721E01131007AB25A /* stack.c in Sources */, 373AD30821C4739500A4EA89 /* NSMutableAttributedString+Styling.m in Sources */, B524AE112352CC7900EA11D4 /* UIScreen+Simplenote.swift in Sources */, @@ -3589,7 +3557,6 @@ B5757368232BED0700443C2E /* UIBezierPath+Simplenote.swift in Sources */, B5476BB723D89AF7000E7723 /* SPSectionHeaderView.swift in Sources */, B5377349252E2A4200BC78C5 /* Value1TableViewCell.swift in Sources */, - BA0F5E0426B62A8B0098C605 /* RemoteError.swift in Sources */, B53C5A5A230330CD00DA2143 /* SPNoteListViewController+Extensions.swift in Sources */, A6C0DFB525C1581D00B9BE39 /* UIScrollView+Simplenote.swift in Sources */, 375D24B621E01131007AB25A /* html_blocks.c in Sources */, @@ -3627,7 +3594,6 @@ B52BB75022CFD18F0042C162 /* UIActivity+Simplenote.swift in Sources */, F9E197D42283D05C0092B3E1 /* CrashLogging.swift in Sources */, BA9B59022685549F00DAD1ED /* StorageSettings.swift in Sources */, - B5699B092C1246C40096D6B7 /* LoginRemote.swift in Sources */, DE7E545B214E34C8008D9928 /* NSString+Count.swift in Sources */, A6E6CE0025A4B0A9005A92DB /* PinLockVerifyController.swift in Sources */, BA55124E2600210B00D8F882 /* TimerFactory.swift in Sources */, @@ -3643,6 +3609,7 @@ 46A3C98F17DFA81A002865AE /* SPNoteListViewController.m in Sources */, B5BD6AF51BF66093004ECE33 /* SPMarkdownPreviewViewController.m in Sources */, B5F3FFFF23F44679007A7C59 /* SPSortBar.swift in Sources */, + B54C1B552C4EE350001E9E18 /* UIAlertController+Auth.swift in Sources */, BA4499C525ED95E5000C563E /* NoticeAction.swift in Sources */, A6E02778256E9487002054DF /* PinLockSetupController.swift in Sources */, B513F2B42319A8D50021CFA4 /* SPNoteTableViewCell.swift in Sources */, @@ -3660,7 +3627,6 @@ BA289B732BE45A3C000E6794 /* IntentsConstants.swift in Sources */, BAF4A96F26DB085D00C51C1D /* NSURLComponents+Simplenote.swift in Sources */, B5E951E124FEDAD4004B10B8 /* UIPasteboard+Note.swift in Sources */, - A6C7648025E9131C00A39067 /* SignupRemote.swift in Sources */, A681C73C2541AC8D00F369C2 /* HuggableTableView.swift in Sources */, 46A3C99917DFA81A002865AE /* SPTransitionController.m in Sources */, B56A695B22F9CD4E00B90398 /* UINavigationBar+Simplenote.swift in Sources */, @@ -3680,6 +3646,7 @@ B5AEC38423FAC5D600D24221 /* DateFormatter+Simplenote.swift in Sources */, B5DF734622A5713600602CE7 /* SortMode.swift in Sources */, BA55B05A25F067DF0042582B /* NoticePresenter.swift in Sources */, + B51D445E2C52CDA800F296A7 /* SPUser+UserProtocol.swift in Sources */, 46A3C99D17DFA81A002865AE /* SPAddCollaboratorsViewController.m in Sources */, B52646AA22D3E04C00EBF299 /* UIViewController+Simplenote.swift in Sources */, B55E428C22A1A4550018C0CE /* SPSortOrderViewController.swift in Sources */, @@ -3689,7 +3656,6 @@ E215C79E180B228800AD36B5 /* SPTextField.m in Sources */, A6DE79CE2552E6CC00BC69C6 /* TagListViewController.swift in Sources */, B513F2B82319A9A40021CFA4 /* UITableViewCell+Simplenote.swift in Sources */, - A628BEC325ED703A00121B64 /* RemoteConstants.swift in Sources */, B51F6DD52460D7540074DDD9 /* NSPredicate+Email.swift in Sources */, B5C2EDF0255AFB6C00C09B32 /* PassthruView.swift in Sources */, A6F4882325A8889E0050CFA8 /* UITextField+Tag.swift in Sources */, @@ -3739,7 +3705,6 @@ 375D24B121E01131007AB25A /* html5_blocks.c in Sources */, B504D4DE23D2014200AEED27 /* IndexPath+Simplenote.swift in Sources */, B5C99A512C45B94600728813 /* AuthenticationMode.swift in Sources */, - A6CC0B0725B8287F00F12A85 /* AccountRemote.swift in Sources */, A61471B925D70C190065D849 /* PopoverViewController.swift in Sources */, B536988D25646C9400817E30 /* CGRect+Simplenote.swift in Sources */, B5E96B611BDE5ACA00D707F5 /* SPMarkdownParser.m in Sources */, @@ -3748,7 +3713,6 @@ B59560E0251A46D500A06788 /* KeychainManager.swift in Sources */, 46A3C9AF17DFA81A002865AE /* NSString+Condensing.m in Sources */, A6F487AF25A79D550050CFA8 /* BiometricAuthentication.swift in Sources */, - BA5768E3269A803F008B510E /* Remote.swift in Sources */, A6F4881325A884CE0050CFA8 /* UITextInput+Simplenote.swift in Sources */, B50F47A41D1D78EA00822748 /* SPRatingsHelper.m in Sources */, 371A8630213DF00E002E9120 /* SPPinLockManager.swift in Sources */, @@ -3763,8 +3727,6 @@ A6CC0B1025B83FE400F12A85 /* AccountVerificationControllerTests.swift in Sources */, A694ABB325D1687200CC3A2D /* NoteScrollPositionCacheTests.swift in Sources */, A6E6CE7525A5B4A3005A92DB /* MockPinLockManager.swift in Sources */, - A6CC0B4425B8505700F12A85 /* MockURLSessionDataTask.swift in Sources */, - A6CC0B3625B8502800F12A85 /* MockURLSession.swift in Sources */, B52F35D322F356F500724793 /* UserDefaults+Tests.swift in Sources */, B511AEE9255A0A5E005B2159 /* InterlinkResultsControllerTests.swift in Sources */, 374F5EF521BF057E00B57E8B /* NSMutableAttributedStringStylingTests.swift in Sources */, @@ -3777,13 +3739,11 @@ BAB017AB260ADDEB007A9CC3 /* MockTimerFactory.swift in Sources */, B55AC57A22D27B9100D8CEB2 /* OptionsTests.swift in Sources */, B543C7DF23CF6AB400003A80 /* NotesListControllerTests.swift in Sources */, - A6CC0B2D25B84FF200F12A85 /* AccountVerificationRemoteTests.swift in Sources */, B5421F4023CE7A1C004DDC19 /* MockupStorage+Sample.swift in Sources */, A6F4883225A891A70050CFA8 /* TagTextFieldInputValidatorTests.swift in Sources */, A6E6CE9125A5B970005A92DB /* PinLockRemoveControllerTests.swift in Sources */, B57401A025B7D9960058960E /* EmailVerificationTests.swift in Sources */, A6E6CEC925A6053A005A92DB /* MockApplication.swift in Sources */, - BA18532826488DBC00D9A347 /* SignupRemoteTests.swift in Sources */, BA3FB8CF25FEA0C500EA9A1B /* NoticeControllerTests.swift in Sources */, BAB32C63269E4190005C72B2 /* RemoteResult+TestHelpers.swift in Sources */, A645FA70254C5E69008A1519 /* NoteContentHelperTests.swift in Sources */, @@ -6278,6 +6238,14 @@ minimumVersion = 1.3.0; }; }; + B51D44592C52CB4000F296A7 /* XCRemoteSwiftPackageReference "SimplenoteEndpoints-Swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "git@github.com:Automattic/SimplenoteEndpoints-Swift.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; B59CACFA2541E05400958330 /* XCRemoteSwiftPackageReference "SimplenoteInterlinks-Swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:Automattic/SimplenoteInterlinks-Swift.git"; @@ -6335,6 +6303,11 @@ package = B50D2FB424E6DFAC00163FC3 /* XCRemoteSwiftPackageReference "SimplenoteSearch-Swift" */; productName = SimplenoteSearch; }; + B51D445A2C52CB4000F296A7 /* SimplenoteEndpoints */ = { + isa = XCSwiftPackageProductDependency; + package = B51D44592C52CB4000F296A7 /* XCRemoteSwiftPackageReference "SimplenoteEndpoints-Swift" */; + productName = SimplenoteEndpoints; + }; B59CACFB2541E05400958330 /* SimplenoteInterlinks */ = { isa = XCSwiftPackageProductDependency; package = B59CACFA2541E05400958330 /* XCRemoteSwiftPackageReference "SimplenoteInterlinks-Swift" */; diff --git a/Simplenote.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Simplenote.xcworkspace/xcshareddata/swiftpm/Package.resolved index cd06d10ef..090b42b39 100644 --- a/Simplenote.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Simplenote.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "5c7dc5a8c3fa899620d4a2168cf121438ee740cb5c2b5fac978ee6263951dbe7", "pins" : [ { "identity" : "automattic-tracks-ios", @@ -27,6 +28,15 @@ "version" : "8.18.0" } }, + { + "identity" : "simplenoteendpoints-swift", + "kind" : "remoteSourceControl", + "location" : "git@github.com:Automattic/SimplenoteEndpoints-Swift.git", + "state" : { + "revision" : "3d1c0a5db39ca798a7de10e7faeef8ae66e941e6", + "version" : "1.0.0" + } + }, { "identity" : "simplenotefoundation-swift", "kind" : "remoteSourceControl", @@ -82,5 +92,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/Simplenote/AccountRemote.swift b/Simplenote/AccountRemote.swift deleted file mode 100644 index d91a16121..000000000 --- a/Simplenote/AccountRemote.swift +++ /dev/null @@ -1,54 +0,0 @@ -import Foundation - -// MARK: - AccountVerificationRemote -// -class AccountRemote: Remote { - // MARK: Performing tasks - - /// Send verification request for specified email address - /// - func verify(email: String, completion: @escaping (_ result: Result) -> Void) { - let request = verificationURLRequest(with: email) - - performDataTask(with: request, completion: completion) - } - - /// Send account deletion request for user - /// - func requestDelete(_ user: SPUser, completion: @escaping (_ result: Result) -> Void) { - let request = deleteRequest(with: user) - performDataTask(with: request, completion: completion) - } - - // MARK: URL Requests - - private func verificationURLRequest(with email: String) -> URLRequest { - let base64EncodedEmail = email.data(using: .utf8)!.base64EncodedString() - let verificationURL = URL(string: SimplenoteConstants.verificationURL)! - - var request = URLRequest(url: verificationURL.appendingPathComponent(base64EncodedEmail), - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: RemoteConstants.timeout) - request.httpMethod = RemoteConstants.Method.GET - - return request - } - - private func deleteRequest(with user: SPUser) -> URLRequest { - let url = URL(string: SimplenoteConstants.accountDeletionURL)! - - var request = URLRequest(url: url, - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: RemoteConstants.timeout) - request.httpMethod = RemoteConstants.Method.POST - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - - let body = [ - "username": user.email.lowercased(), - "token": user.authToken - ] - request.httpBody = try? JSONEncoder().encode(body) - - return request - } -} diff --git a/Simplenote/Classes/MagicLinkAuthenticator.swift b/Simplenote/Classes/MagicLinkAuthenticator.swift index b5ba213f2..50046956e 100644 --- a/Simplenote/Classes/MagicLinkAuthenticator.swift +++ b/Simplenote/Classes/MagicLinkAuthenticator.swift @@ -1,4 +1,5 @@ import Foundation +import SimplenoteEndpoints // MARK: - Notifications diff --git a/Simplenote/Classes/RemoteConstants.swift b/Simplenote/Classes/RemoteConstants.swift deleted file mode 100644 index 4a081c0c2..000000000 --- a/Simplenote/Classes/RemoteConstants.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Foundation - -// MARK: Remote Constants -// -struct RemoteConstants { - struct Method { - static let GET = "GET" - static let POST = "POST" - } - - static let timeout: TimeInterval = 30 -} diff --git a/Simplenote/Classes/SPAuthError.swift b/Simplenote/Classes/SPAuthError.swift index dbea55c53..66c80ef8b 100644 --- a/Simplenote/Classes/SPAuthError.swift +++ b/Simplenote/Classes/SPAuthError.swift @@ -1,4 +1,6 @@ import Foundation +import SimplenoteEndpoints + // MARK: - SPAuthError // @@ -11,17 +13,38 @@ enum SPAuthError: Error { case unverifiedEmail case tooManyAttempts case generic + case requestNotFound + case invalidCode case unknown(statusCode: Int, response: String?, error: Error?) } + // MARK: - SPAuthError Convenience Initializers // extension SPAuthError { - /// Returns the SPAuthError matching a given Simperium Login Error Code + /// Returns an AuthError matching a RemoteError (Login) Counterpart + /// + init(loginRemoteError: RemoteError) { + self = SPAuthError(loginErrorCode: loginRemoteError.statusCode, response: loginRemoteError.response, error: loginRemoteError.networkError) + } + + /// Returns an AuthError matching a RemoteError (Signup) Counterpart + /// + init(signupRemoteError: RemoteError) { + self = SPAuthError(signupErrorCode: signupRemoteError.statusCode, response: signupRemoteError.response, error: signupRemoteError.networkError) + } + + /// Returns the AuthError matching a given Simperium Login Error Code /// init(loginErrorCode: Int, response: String?, error: Error?) { switch loginErrorCode { + case .zero: + self = .network + case 400 where response == Constants.requestNotFound: + self = .requestNotFound + case 400 where response == Constants.invalidCode: + self = .invalidCode case 401 where response == Constants.compromisedPassword: self = .compromisedPassword case 401: @@ -35,10 +58,12 @@ extension SPAuthError { } } - /// Returns the SPAuthError matching a given Simperium Signup Error Code + /// Returns the AuthError matching a given a Signup Error Code /// init(signupErrorCode: Int, response: String?, error: Error?) { switch signupErrorCode { + case .zero: + self = .network case 401: self = .signupBadCredentials case 409: @@ -51,6 +76,7 @@ extension SPAuthError { } } + // MARK: - SPAuthError Public Methods // extension SPAuthError { @@ -90,6 +116,10 @@ extension SPAuthError { return NSLocalizedString("You must verify your email before being able to login.", comment: "Error for un verified email") case .tooManyAttempts: return NSLocalizedString("Too many login attempts. Try again later.", comment: "Error message for too many login attempts") + case .requestNotFound: + return NSLocalizedString("Your authentication code has expired. Please request a new one", comment: "Error message for Invalid Login Code") + case .invalidCode: + return NSLocalizedString("The code you've entered is not correct. Please try again", comment: "Error message for Invalid Login Code") default: return NSLocalizedString("We're having problems. Please try again soon.", comment: "Generic error") } @@ -99,4 +129,6 @@ extension SPAuthError { private struct Constants { static let compromisedPassword = "compromised password" static let requiresVerification = "verification required" + static let requestNotFound = "request-not-found" + static let invalidCode = "invalid-code" } diff --git a/Simplenote/Classes/SPAuthHandler.swift b/Simplenote/Classes/SPAuthHandler.swift index 899b30b54..5d0fcdd9c 100644 --- a/Simplenote/Classes/SPAuthHandler.swift +++ b/Simplenote/Classes/SPAuthHandler.swift @@ -1,5 +1,6 @@ import Foundation import SafariServices +import SimplenoteEndpoints // MARK: - SPAuthHandler // @@ -55,15 +56,13 @@ class SPAuthHandler { /// Requests an Authentication Email /// - func requestLoginEmail(username: String, onCompletion: @escaping (SPAuthError?) -> Void) { + @MainActor + func requestLoginEmail(username: String) async throws { let remote = LoginRemote() - remote.requestLoginEmail(email: username) { (result) in - switch result { - case .success: - onCompletion(nil) - case .failure(let error): - onCompletion(self.authenticationError(for: error)) - } + do { + try await remote.requestLoginEmail(email: username) + } catch let remoteError as RemoteError { + throw SPAuthError(loginRemoteError: remoteError) } } @@ -76,8 +75,8 @@ class SPAuthHandler { let confirmation = try await remote.requestLoginConfirmation(email: username, authCode: code.uppercased()) simperiumService.authenticate(withUsername: confirmation.username, token: confirmation.syncToken) - } catch let error as RemoteError { - throw authenticationError(for: error) + } catch let remoteError as RemoteError { + throw SPAuthError(loginRemoteError: remoteError) } } @@ -90,27 +89,17 @@ class SPAuthHandler { /// - onCompletion: Closure to be executed on completion /// func signupWithCredentials(username: String, onCompletion: @escaping (SPAuthError?) -> Void) { - SignupRemote().signup(with: username) { (result) in + SignupRemote().requestSignup(email: username) { (result) in switch result { case .success: onCompletion(nil) - case .failure(let error): - onCompletion(self.authenticationError(for: error)) + case .failure(let remoteError): + let error = SPAuthError(signupRemoteError: remoteError) + onCompletion(error) } } } - private func authenticationError(for remoteError: RemoteError) -> SPAuthError { - switch remoteError { - case .network: - return .network - case .tooManyAttempts: - return .tooManyAttempts - case .requestError(let statusCode, let error): - return SPAuthError(signupErrorCode: statusCode, response: error?.localizedDescription, error: error) - } - } - /// Presents the Password Reset (Web) Interface /// func presentPasswordReset(from sourceViewController: UIViewController, username: String) { diff --git a/Simplenote/Classes/SPAuthViewController.swift b/Simplenote/Classes/SPAuthViewController.swift index c3f50f191..e3a323041 100644 --- a/Simplenote/Classes/SPAuthViewController.swift +++ b/Simplenote/Classes/SPAuthViewController.swift @@ -2,6 +2,8 @@ import Foundation import UIKit import SafariServices import SwiftUI +import SimplenoteEndpoints + // MARK: - SPAuthViewController // @@ -433,24 +435,52 @@ extension SPAuthViewController { guard ensureWarningsAreOnScreenWhenNeeded() else { return } - + + Task { @MainActor in + await requestLogInCodeAsync() + } + } + + @MainActor + private func requestLogInCodeAsync() async { lockdownInterface() - controller.requestLoginEmail(username: email) { error in - switch error { - case .none: - self.presentCodeInterface() - SPTracker.trackLoginLinkRequested() - - case .tooManyAttempts: - self.presentPasswordInterfaceWithRateLimitingHeader() - - case .some(let error): - self.handleError(error: error) - } + do { + try await controller.requestLoginEmail(username: email) + self.presentCodeInterface() + SPTracker.trackLoginLinkRequested() + + } catch SPAuthError.tooManyAttempts { + self.presentPasswordInterfaceWithRateLimitingHeader() - self.unlockInterface() + } catch { + let error = error as? SPAuthError ?? .generic + self.handleError(error: error) } + + self.unlockInterface() + } + + /// Requests a new Login Code, without pushing any secondary UI on success + /// + @IBAction func requestLogInCodeAndDontPush() { + Task { @MainActor in + await self.requestLogInCodeAndDontPushAsync() + } + } + + /// Requests a new Login Code, without pushing any secondary UI on success. Asynchronous API! + /// + @MainActor + private func requestLogInCodeAndDontPushAsync() async { + do { + try await controller.requestLoginEmail(username: email) + } catch { + let error = error as? SPAuthError ?? .generic + self.handleError(error: error) + } + + SPTracker.trackLoginLinkRequested() } @IBAction func performLogInWithCode() { @@ -517,7 +547,7 @@ extension SPAuthViewController { } @IBAction func presentPasswordInterface() { - presentPasswordInterfaceWithHeader(header: nil) + presentPasswordInterfaceWithHeader(header: AuthenticationStrings.loginWithEmailEmailHeader) } @IBAction func presentPasswordInterfaceWithRateLimitingHeader() { @@ -613,6 +643,8 @@ private extension SPAuthViewController { presentPasswordCompromisedError(error: error) case .unverifiedEmail: presentUserUnverifiedError(error: error, email: email) + case .requestNotFound: + presentLoginCodeExpiredError() case .unknown(let statusCode, let response, let error) where debugEnabled: let details = NSAttributedString.stringFromNetworkError(statusCode: statusCode, response: response, error: error) presentDebugDetails(details: details) @@ -640,6 +672,14 @@ private extension SPAuthViewController { present(alertController, animated: true, completion: nil) } + + func presentLoginCodeExpiredError() { + let alertController = UIAlertController.buildLoginCodeNotFoundAlert { + self.navigationController?.popViewController(animated: true) + } + + present(alertController, animated: true, completion: nil) + } func presentUserUnverifiedError(error: SPAuthError, email: String) { let alertController = UIAlertController(title: error.title, message: error.message, preferredStyle: .alert) @@ -854,6 +894,7 @@ private enum AuthenticationStrings { static let acceptActionText = NSLocalizedString("Accept", comment: "Accept Action") static let cancelActionText = NSLocalizedString("Cancel", comment: "Cancel Action") static let loginActionText = NSLocalizedString("Log In", comment: "Log In Action") + static let loginWithEmailEmailHeader = NSLocalizedString("Enter the password for the account {{EMAIL}}", comment: "Header for Login With Password. Please preserve the {{EMAIL}} substring") static let loginWithEmailLimitHeader = NSLocalizedString("Log in with email failed, please enter your password", comment: "Header for Enter Password UI, when the user performed too many requests") static let compromisedAlertCancel = NSLocalizedString("Cancel", comment: "Cancel action for password alert") static let compromisedAlertReset = NSLocalizedString("Change Password", comment: "Change password action") diff --git a/Simplenote/Classes/SPSettingsViewController+Extensions.swift b/Simplenote/Classes/SPSettingsViewController+Extensions.swift index fb9bcead3..7c0bb73a9 100644 --- a/Simplenote/Classes/SPSettingsViewController+Extensions.swift +++ b/Simplenote/Classes/SPSettingsViewController+Extensions.swift @@ -1,4 +1,6 @@ import UIKit +import SimplenoteEndpoints + // MARK: - Subscriber UI // @@ -188,12 +190,7 @@ extension SPSettingsViewController { } private func handleError(_ error: RemoteError) { - switch error { - case .network: - NoticeController.shared.present(NoticeFactory.networkError()) - default: - presentRequestErrorAlert() - } + presentRequestErrorAlert() } private func presentSuccessAlert(for user: SPUser) { diff --git a/Simplenote/Classes/SignupRemote.swift b/Simplenote/Classes/SignupRemote.swift deleted file mode 100644 index 8a947250a..000000000 --- a/Simplenote/Classes/SignupRemote.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -// MARK: - SignupRemote -// -class SignupRemote: Remote { - func signup(with email: String, completion: @escaping (_ result: Result) -> Void) { - let urlRequest = request(with: email) - - performDataTask(with: urlRequest, completion: completion) - } - - private func request(with email: String) -> URLRequest { - let url = URL(string: SimplenoteConstants.signupURL)! - - var request = URLRequest(url: url, - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: RemoteConstants.timeout) - request.httpMethod = RemoteConstants.Method.POST - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - request.httpBody = try? JSONEncoder().encode(["username": email.lowercased()]) - - return request - } -} diff --git a/Simplenote/Controllers/AccountDeletionController.swift b/Simplenote/Controllers/AccountDeletionController.swift index c5251a05d..ac248de09 100644 --- a/Simplenote/Controllers/AccountDeletionController.swift +++ b/Simplenote/Controllers/AccountDeletionController.swift @@ -1,4 +1,5 @@ import Foundation +import SimplenoteEndpoints @objc class AccountDeletionController: NSObject { diff --git a/Simplenote/LoginRemote.swift b/Simplenote/LoginRemote.swift deleted file mode 100644 index b69fce3f6..000000000 --- a/Simplenote/LoginRemote.swift +++ /dev/null @@ -1,46 +0,0 @@ -import Foundation - -// MARK: - LoginRemote -// -class LoginRemote: Remote { - - func requestLoginEmail(email: String, completion: @escaping (_ result: Result) -> Void) { - let request = requestForLoginRequest(email: email) - performDataTask(with: request, completion: completion) - } - - func requestLoginConfirmation(email: String, authCode: String) async throws -> LoginConfirmationResponse { - let request = requestForLoginCompletion(email: email, authCode: authCode) - return try await performDataTask(with: request, type: LoginConfirmationResponse.self) - } -} - - -// MARK: - LoginConfirmationResponse -// -struct LoginConfirmationResponse: Decodable { - let username: String - let syncToken: String -} - - -// MARK: - Private API(s) -// -private extension LoginRemote { - - func requestForLoginRequest(email: String) -> URLRequest { - let url = URL(string: SimplenoteConstants.loginRequestURL)! - return requestForURL(url, method: RemoteConstants.Method.POST, httpBody: [ - "request_source": SimplenoteConstants.simplenotePlatformName, - "username": email.lowercased() - ]) - } - - func requestForLoginCompletion(email: String, authCode: String) -> URLRequest { - let url = URL(string: SimplenoteConstants.loginCompletionURL)! - return requestForURL(url, method: RemoteConstants.Method.POST, httpBody: [ - "auth_code": authCode, - "username": email - ]) - } -} diff --git a/Simplenote/Remote.swift b/Simplenote/Remote.swift deleted file mode 100644 index 5c32883ea..000000000 --- a/Simplenote/Remote.swift +++ /dev/null @@ -1,73 +0,0 @@ -import Foundation - -class Remote { - private let urlSession: URLSession - - init(urlSession: URLSession = URLSession.shared) { - self.urlSession = urlSession - } - - /// Send task for remote - /// Sublcassing Notes: To be able to send a task it is required to first setup the URL request for the task to use - /// - func performDataTask(with request: URLRequest, completion: @escaping (_ result: Result) -> Void) { - let dataTask = urlSession.dataTask(with: request) { (data, response, dataTaskError) in - DispatchQueue.main.async { - - if let error = RemoteError(statusCode: response?.responseStatusCode ?? .zero, error: dataTaskError) { - completion(.failure(error)) - return - } - - completion(.success(data)) - } - } - - dataTask.resume() - } - - /// Performs a URLSession Data Task - /// - func performDataTask(with request: URLRequest) async throws -> Data { - let (data, response) = try await urlSession.data(for: request) - - if let error = RemoteError(statusCode: response.responseStatusCode) { - throw error - } - - return data - } - - /// Performs a URLSession Data Task, and decodes a given Type - /// - func performDataTask(with request: URLRequest, type: T.Type) async throws -> T { - let data = try await performDataTask(with: request) - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - return try decoder.decode(type, from: data) - } - - /// Builds a URLRequest for the specified URL / Method / params - /// - func requestForURL(_ url: URL, method: String, httpBody: [String: String]?) -> URLRequest { - var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: RemoteConstants.timeout) - - request.httpMethod = method - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - - if let httpBody { - request.httpBody = try? JSONEncoder().encode(httpBody) - } - - return request - } -} - - -extension URLResponse { - - var responseStatusCode: Int { - (self as? HTTPURLResponse)?.statusCode ?? .zero - } -} diff --git a/Simplenote/RemoteError.swift b/Simplenote/RemoteError.swift deleted file mode 100644 index 4366d98e1..000000000 --- a/Simplenote/RemoteError.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation - -enum RemoteError: Error { - case network - case tooManyAttempts - case requestError(Int, Error?) -} - - -extension RemoteError { - - init?(statusCode: Int, error: Error? = nil) { - if statusCode / 100 == 2 { - return nil - } - - switch statusCode { - case 429: - self = .tooManyAttempts - default: - self = statusCode > 0 ? .requestError(statusCode, error) : .network - } - } -} - - -extension RemoteError: Equatable { - static func == (lhs: RemoteError, rhs: RemoteError) -> Bool { - switch (lhs, rhs) { - case (.network, .network): - return true - case (.requestError(let lhsStatus, let lhsError), .requestError(let rhsStatus, let rhsError)): - return lhsStatus == rhsStatus && lhsError?.localizedDescription == rhsError?.localizedDescription - default: - return false - } - } -} diff --git a/Simplenote/SPUser+UserProtocol.swift b/Simplenote/SPUser+UserProtocol.swift new file mode 100644 index 000000000..b555a4e96 --- /dev/null +++ b/Simplenote/SPUser+UserProtocol.swift @@ -0,0 +1,8 @@ +import Foundation +import SimplenoteEndpoints +import Simperium + + +// MARK: - Simperium's SPUser Conformance +// +extension SPUser: UserProtocol { } diff --git a/Simplenote/UIAlertController+Auth.swift b/Simplenote/UIAlertController+Auth.swift new file mode 100644 index 000000000..73702271f --- /dev/null +++ b/Simplenote/UIAlertController+Auth.swift @@ -0,0 +1,22 @@ +import Foundation + + +// MARK: - UIAlertController Helpers +// +extension UIAlertController { + + /// Builds an alert indicating that the Login Code has Expired + /// + static func buildLoginCodeNotFoundAlert(onRequestCode: @escaping () -> Void) -> UIAlertController { + let title = NSLocalizedString("Sorry!", comment: "Email TextField Placeholder") + let message = NSLocalizedString("The authentication code you've requested has expired. Please request a new one", comment: "Email TextField Placeholder") + let acceptText = NSLocalizedString("Accept", comment: "Accept Message") + + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addDefaultActionWithTitle(acceptText) { _ in + onRequestCode() + } + + return alertController + } +} diff --git a/Simplenote/URLRequest+Simplenote.swift b/Simplenote/URLRequest+Simplenote.swift deleted file mode 100644 index 5adddd4cf..000000000 --- a/Simplenote/URLRequest+Simplenote.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -extension URLRequest { - func decodeHtmlBody() throws -> T? { - guard let _ = httpBody else { - return nil - } - - return try httpBody.map { - try JSONDecoder().decode(T.self, from: $0) - } - } -} diff --git a/Simplenote/Verification/AccountVerificationController.swift b/Simplenote/Verification/AccountVerificationController.swift index 4bab434a6..3f11cc042 100644 --- a/Simplenote/Verification/AccountVerificationController.swift +++ b/Simplenote/Verification/AccountVerificationController.swift @@ -1,4 +1,5 @@ import Foundation +import SimplenoteEndpoints // MARK: - AccountVerificationController // diff --git a/SimplenoteTests/Network/MockURLSession.swift b/SimplenoteTests/Network/MockURLSession.swift deleted file mode 100644 index 70b5042d2..000000000 --- a/SimplenoteTests/Network/MockURLSession.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -// MARK: - MockURLSession -// -class MockURLSession: URLSession { - var data: (Data?, URLResponse?, Error?)? - var lastRequest: URLRequest? - - override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { - lastRequest = request - return MockURLSessionDataTask { - completionHandler(self.data?.0, self.data?.1, self.data?.2) - } - } -} diff --git a/SimplenoteTests/Network/MockURLSessionDataTask.swift b/SimplenoteTests/Network/MockURLSessionDataTask.swift deleted file mode 100644 index 32a97ea9a..000000000 --- a/SimplenoteTests/Network/MockURLSessionDataTask.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -// MARK: - MockURLSessionDataTask -// -class MockURLSessionDataTask: URLSessionDataTask { - private let completion: () -> Void - - init(completion: @escaping () -> Void) { - self.completion = completion - } - - override func resume() { - completion() - } -} diff --git a/SimplenoteTests/RemoteResult+TestHelpers.swift b/SimplenoteTests/RemoteResult+TestHelpers.swift index 585b9ae16..ba37ff02f 100644 --- a/SimplenoteTests/RemoteResult+TestHelpers.swift +++ b/SimplenoteTests/RemoteResult+TestHelpers.swift @@ -1,4 +1,5 @@ import Foundation +import SimplenoteEndpoints @testable import Simplenote extension Remote { @@ -7,6 +8,6 @@ extension Remote { return .success(nil) } - return .failure(RemoteError.requestError(0, nil)) + return .failure(RemoteError(statusCode: 0, response: nil, networkError: nil)) } } diff --git a/SimplenoteTests/SignupRemoteTests.swift b/SimplenoteTests/SignupRemoteTests.swift deleted file mode 100644 index 239dbb818..000000000 --- a/SimplenoteTests/SignupRemoteTests.swift +++ /dev/null @@ -1,75 +0,0 @@ -import XCTest -@testable import Simplenote - -class SignupRemoteTests: XCTestCase { - private lazy var urlSession = MockURLSession() - private lazy var signupRemote = SignupRemote(urlSession: urlSession) - - func testSuccessWhenStatusCodeIs2xx() { - for _ in 0..<5 { - test(withStatusCode: Int.random(in: 200..<300), email: "email@gmail.com", expectedResult: .success(nil)) - } - } - - func testFailureWhenStatusCodeIs4xxOr5xx() { - for _ in 0..<5 { - let statusCode = Int.random(in: 400..<600) - test(withStatusCode: statusCode, email: "email@gmail.com", expectedResult: .failure(RemoteError.requestError(statusCode, nil))) - } - } - - func testRequestSetsEmailToCorrectCase() throws { - signupRemote.signup(with: "EMAIL@gmail.com", completion: { _ in }) - - let expecation = "email@gmail.com" - let body: Dictionary = try XCTUnwrap(urlSession.lastRequest?.decodeHtmlBody()) - let decodedEmail = try XCTUnwrap(body["username"]) - - XCTAssertEqual(expecation, decodedEmail) - } - - func testRequestSetsEmailToCorrectCaseWithSpecialCharacters() throws { - signupRemote.signup(with: "EMAIL123456@#$%^@gmail.com", completion: { _ in }) - - let expecation = "email123456@#$%^@gmail.com" - let body: Dictionary = try XCTUnwrap(urlSession.lastRequest?.decodeHtmlBody()) - let decodedEmail = try XCTUnwrap(body["username"]) - - XCTAssertEqual(expecation, decodedEmail) - } - - func testRequestSetsEmailToCorrectCaseWithMixedCase() throws { - signupRemote.signup(with: "eMaIl@gmail.com", completion: { _ in }) - - let expecation = "email@gmail.com" - let body: Dictionary = try XCTUnwrap(urlSession.lastRequest?.decodeHtmlBody()) - let decodedEmail = try XCTUnwrap(body["username"]) - - XCTAssertEqual(expecation, decodedEmail) - } - - private func test(withStatusCode statusCode: Int?, email: String, expectedResult: Result) { - urlSession.data = (nil, - response(with: statusCode), - nil) - - let expectation = self.expectation(description: "Verify is called") - - signupRemote.signup(with: email) { (result) in - XCTAssertEqual(result, expectedResult) - expectation.fulfill() - } - - waitForExpectations(timeout: Constants.expectationTimeout, handler: nil) - } - - private func response(with statusCode: Int?) -> HTTPURLResponse? { - guard let statusCode = statusCode else { - return nil - } - return HTTPURLResponse(url: URL(fileURLWithPath: "/"), - statusCode: statusCode, - httpVersion: nil, - headerFields: nil) - } -} diff --git a/SimplenoteTests/Verification/AccountVerificationControllerTests.swift b/SimplenoteTests/Verification/AccountVerificationControllerTests.swift index 39790a9ef..fe5b29921 100644 --- a/SimplenoteTests/Verification/AccountVerificationControllerTests.swift +++ b/SimplenoteTests/Verification/AccountVerificationControllerTests.swift @@ -1,4 +1,5 @@ import XCTest +import SimplenoteEndpoints @testable import Simplenote // MARK: - AccountVerificationControllerTests diff --git a/SimplenoteTests/Verification/AccountVerificationRemoteTests.swift b/SimplenoteTests/Verification/AccountVerificationRemoteTests.swift deleted file mode 100644 index 6443b0d14..000000000 --- a/SimplenoteTests/Verification/AccountVerificationRemoteTests.swift +++ /dev/null @@ -1,51 +0,0 @@ -import XCTest -@testable import Simplenote - -// MARK: - AccountVerificationRemoteTests -// -class AccountVerificationRemoteTests: XCTestCase { - private lazy var urlSession = MockURLSession() - private lazy var remote = AccountRemote(urlSession: urlSession) - - func testSuccessWhenStatusCodeIs2xx() { - for _ in 0..<5 { - test(withStatusCode: Int.random(in: 200..<300), expectedResult: .success(nil)) - } - } - - func testFailureWhenStatusCodeIs4xxOr5xx() { - for _ in 0..<5 { - let statusCode = Int.random(in: 400..<600) - test(withStatusCode: statusCode, expectedResult: .failure(RemoteError.requestError(statusCode, nil))) - } - } - - func testFailureWhenNoResponse() { - test(withStatusCode: nil, expectedResult: .failure(RemoteError.network)) - } - - private func test(withStatusCode statusCode: Int?, expectedResult: Result) { - urlSession.data = (nil, - response(with: statusCode), - nil) - - let expectation = self.expectation(description: "Verify is called") - - remote.verify(email: UUID().uuidString) { (result) in - XCTAssertEqual(result, expectedResult) - expectation.fulfill() - } - - waitForExpectations(timeout: 3, handler: nil) - } - - private func response(with statusCode: Int?) -> HTTPURLResponse? { - guard let statusCode = statusCode else { - return nil - } - return HTTPURLResponse(url: URL(fileURLWithPath: "/"), - statusCode: statusCode, - httpVersion: nil, - headerFields: nil) - } -} diff --git a/SimplenoteTests/Verification/Helpers/MockAccountVerificationRemote.swift b/SimplenoteTests/Verification/Helpers/MockAccountVerificationRemote.swift index f09f4f411..d4add1b4e 100644 --- a/SimplenoteTests/Verification/Helpers/MockAccountVerificationRemote.swift +++ b/SimplenoteTests/Verification/Helpers/MockAccountVerificationRemote.swift @@ -1,4 +1,5 @@ import XCTest +@testable import SimplenoteEndpoints @testable import Simplenote class MockAccountVerificationRemote: AccountRemote {