feat: [WIP] add authentication scene
This commit is contained in:
parent
b749d0a7bc
commit
36c1807182
|
@ -11,7 +11,7 @@
|
||||||
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
||||||
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* TimelinePostView.swift */; };
|
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* TimelinePostView.swift */; };
|
||||||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
||||||
2D42FF6125C8177C004A627A /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 2D42FF6025C8177C004A627A /* SwiftPackageProductDependency */; };
|
2D42FF6125C8177C004A627A /* ActiveLabel in Frameworks */ = {isa = PBXBuildFile; productRef = 2D42FF6025C8177C004A627A /* ActiveLabel */; };
|
||||||
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF6A25C817D2004A627A /* MastodonContent.swift */; };
|
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF6A25C817D2004A627A /* MastodonContent.swift */; };
|
||||||
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF7D25C82218004A627A /* ActionToolBarContainer.swift */; };
|
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF7D25C82218004A627A /* ActionToolBarContainer.swift */; };
|
||||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */; };
|
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */; };
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46976325C2A71500CF4AA9 /* UIIamge.swift */; };
|
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46976325C2A71500CF4AA9 /* UIIamge.swift */; };
|
||||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */; };
|
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */; };
|
||||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
||||||
2D61336925C18A4F00CAE157 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* SwiftPackageProductDependency */; };
|
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; };
|
||||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
||||||
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */; };
|
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */; };
|
||||||
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */; };
|
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */; };
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||||
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||||
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
|
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
|
||||||
5D526FE225BE9AC400460CB9 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* SwiftPackageProductDependency */; };
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
||||||
5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; };
|
5E0DEC05797A7E6933788DDB /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 452147B2903DF38070FE56A2 /* Pods_MastodonTests.framework */; };
|
||||||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||||
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */; };
|
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */; };
|
||||||
|
@ -43,12 +43,10 @@
|
||||||
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
|
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
|
||||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
|
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
|
||||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */; };
|
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */; };
|
||||||
DB0140BD25C40D7500F9F3CF /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = DB0140BC25C40D7500F9F3CF /* SwiftPackageProductDependency */; };
|
DB0140BD25C40D7500F9F3CF /* CommonOSLog in Frameworks */ = {isa = PBXBuildFile; productRef = DB0140BC25C40D7500F9F3CF /* CommonOSLog */; };
|
||||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
|
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
|
||||||
DB3D0FF325BAA61700EAA174 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* SwiftPackageProductDependency */; };
|
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
||||||
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
||||||
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D102225BAA7B400EAA174 /* Assets.swift */; };
|
|
||||||
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D102325BAA7B400EAA174 /* Strings.swift */; };
|
|
||||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD525BAA00100D1B89D /* AppDelegate.swift */; };
|
||||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD725BAA00100D1B89D /* SceneDelegate.swift */; };
|
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DD725BAA00100D1B89D /* SceneDelegate.swift */; };
|
||||||
DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDB25BAA00100D1B89D /* Main.storyboard */; };
|
DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DB427DDB25BAA00100D1B89D /* Main.storyboard */; };
|
||||||
|
@ -80,6 +78,12 @@
|
||||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
||||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
||||||
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */; };
|
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */; };
|
||||||
|
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98336A25C9420100AD9700 /* APIService+App.swift */; };
|
||||||
|
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
||||||
|
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
||||||
|
DB98338725C945ED00AD9700 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338525C945ED00AD9700 /* Strings.swift */; };
|
||||||
|
DB98338825C945ED00AD9700 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338625C945ED00AD9700 /* Assets.swift */; };
|
||||||
|
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98339B25C96DE600AD9700 /* APIService+Account.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -176,8 +180,6 @@
|
||||||
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
|
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
|
||||||
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
||||||
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
DB3D102225BAA7B400EAA174 /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
|
||||||
DB3D102325BAA7B400EAA174 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
|
||||||
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
DB427DD225BAA00100D1B89D /* Mastodon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mastodon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DB427DD525BAA00100D1B89D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
DB427DD525BAA00100D1B89D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
DB427DD725BAA00100D1B89D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
DB427DD725BAA00100D1B89D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
@ -217,6 +219,12 @@
|
||||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
||||||
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = "<group>"; };
|
||||||
|
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
||||||
|
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
||||||
|
DB98338525C945ED00AD9700 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||||
|
DB98338625C945ED00AD9700 /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
||||||
|
DB98339B25C96DE600AD9700 /* APIService+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Account.swift"; sourceTree = "<group>"; };
|
||||||
DBF53F5F25C14E88008AAC7B /* Mastodon.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Mastodon.xctestplan; path = Mastodon/Mastodon.xctestplan; sourceTree = "<group>"; };
|
DBF53F5F25C14E88008AAC7B /* Mastodon.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Mastodon.xctestplan; path = Mastodon/Mastodon.xctestplan; sourceTree = "<group>"; };
|
||||||
DBF53F6025C14E9D008AAC7B /* MastodonSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MastodonSDK.xctestplan; sourceTree = "<group>"; };
|
DBF53F6025C14E9D008AAC7B /* MastodonSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MastodonSDK.xctestplan; sourceTree = "<group>"; };
|
||||||
EC6E707B68A67DB08EC288FA /* Pods-MastodonTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.debug.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.debug.xcconfig"; sourceTree = "<group>"; };
|
EC6E707B68A67DB08EC288FA /* Pods-MastodonTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.debug.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
@ -227,13 +235,13 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
DB0140BD25C40D7500F9F3CF /* BuildFile in Frameworks */,
|
DB0140BD25C40D7500F9F3CF /* CommonOSLog in Frameworks */,
|
||||||
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
||||||
2D42FF6125C8177C004A627A /* BuildFile in Frameworks */,
|
2D42FF6125C8177C004A627A /* ActiveLabel in Frameworks */,
|
||||||
5D526FE225BE9AC400460CB9 /* BuildFile in Frameworks */,
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
||||||
2D61336925C18A4F00CAE157 /* BuildFile in Frameworks */,
|
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
|
||||||
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
|
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
|
||||||
DB3D0FF325BAA61700EAA174 /* BuildFile in Frameworks */,
|
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */,
|
||||||
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */,
|
45B49097460EDE530AD5AA72 /* Pods_Mastodon.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -315,6 +323,10 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2D61335D25C1894B00CAE157 /* APIService.swift */,
|
2D61335D25C1894B00CAE157 /* APIService.swift */,
|
||||||
|
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */,
|
||||||
|
DB98336A25C9420100AD9700 /* APIService+App.swift */,
|
||||||
|
DB98337025C9443200AD9700 /* APIService+Authentication.swift */,
|
||||||
|
DB98339B25C96DE600AD9700 /* APIService+Account.swift */,
|
||||||
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */,
|
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */,
|
||||||
2D61335625C1887F00CAE157 /* Persist */,
|
2D61335625C1887F00CAE157 /* Persist */,
|
||||||
);
|
);
|
||||||
|
@ -444,15 +456,6 @@
|
||||||
path = Resources;
|
path = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
DB3D101B25BAA79200EAA174 /* Generated */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DB3D102225BAA7B400EAA174 /* Assets.swift */,
|
|
||||||
DB3D102325BAA7B400EAA174 /* Strings.swift */,
|
|
||||||
);
|
|
||||||
path = Generated;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DB427DC925BAA00100D1B89D = {
|
DB427DC925BAA00100D1B89D = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -467,6 +470,7 @@
|
||||||
DB427DD325BAA00100D1B89D /* Products */,
|
DB427DD325BAA00100D1B89D /* Products */,
|
||||||
1EBA4F56E920856A3FC84ACB /* Pods */,
|
1EBA4F56E920856A3FC84ACB /* Pods */,
|
||||||
3FE14AD363ED19AE7FF210A6 /* Frameworks */,
|
3FE14AD363ED19AE7FF210A6 /* Frameworks */,
|
||||||
|
DB98335F25C93B0400AD9700 /* Recovered References */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -485,15 +489,15 @@
|
||||||
DB427DD425BAA00100D1B89D /* Mastodon */ = {
|
DB427DD425BAA00100D1B89D /* Mastodon */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
|
||||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||||
|
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
||||||
2D76319C25C151DE00929FB9 /* Diffiable */,
|
2D76319C25C151DE00929FB9 /* Diffiable */,
|
||||||
DB8AF52A25C13561002E6C99 /* State */,
|
DB8AF52A25C13561002E6C99 /* State */,
|
||||||
2D61335525C1886800CAE157 /* Service */,
|
2D61335525C1886800CAE157 /* Service */,
|
||||||
DB8AF56225C138BC002E6C99 /* Extension */,
|
|
||||||
DB8AF55525C1379F002E6C99 /* Scene */,
|
DB8AF55525C1379F002E6C99 /* Scene */,
|
||||||
DB8AF54125C13647002E6C99 /* Coordinator */,
|
DB8AF54125C13647002E6C99 /* Coordinator */,
|
||||||
DB3D101B25BAA79200EAA174 /* Generated */,
|
DB8AF56225C138BC002E6C99 /* Extension */,
|
||||||
|
DB98338425C945ED00AD9700 /* Generated */,
|
||||||
DB3D0FF825BAA6B200EAA174 /* Resources */,
|
DB3D0FF825BAA6B200EAA174 /* Resources */,
|
||||||
DB3D0FF725BAA68500EAA174 /* Supporting Files */,
|
DB3D0FF725BAA68500EAA174 /* Supporting Files */,
|
||||||
);
|
);
|
||||||
|
@ -578,9 +582,9 @@
|
||||||
DB8AF52A25C13561002E6C99 /* State */ = {
|
DB8AF52A25C13561002E6C99 /* State */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DB8AF52D25C13561002E6C99 /* AppContext.swift */,
|
||||||
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */,
|
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */,
|
||||||
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */,
|
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */,
|
||||||
DB8AF52D25C13561002E6C99 /* AppContext.swift */,
|
|
||||||
);
|
);
|
||||||
path = State;
|
path = State;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -606,10 +610,10 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2D7631A425C1532200929FB9 /* Share */,
|
2D7631A425C1532200929FB9 /* Share */,
|
||||||
|
DB01409B25C40BB600F9F3CF /* Authentication */,
|
||||||
2D76316325C14BAC00929FB9 /* PublicTimeline */,
|
2D76316325C14BAC00929FB9 /* PublicTimeline */,
|
||||||
DB8AF54E25C13703002E6C99 /* MainTab */,
|
DB8AF54E25C13703002E6C99 /* MainTab */,
|
||||||
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
|
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
|
||||||
DB01409B25C40BB600F9F3CF /* Authentication */,
|
|
||||||
);
|
);
|
||||||
path = Scene;
|
path = Scene;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -628,6 +632,23 @@
|
||||||
path = Extension;
|
path = Extension;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
DB98335F25C93B0400AD9700 /* Recovered References */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */,
|
||||||
|
);
|
||||||
|
name = "Recovered References";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DB98338425C945ED00AD9700 /* Generated */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DB98338525C945ED00AD9700 /* Strings.swift */,
|
||||||
|
DB98338625C945ED00AD9700 /* Assets.swift */,
|
||||||
|
);
|
||||||
|
path = Generated;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXHeadersBuildPhase section */
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
@ -661,11 +682,11 @@
|
||||||
);
|
);
|
||||||
name = Mastodon;
|
name = Mastodon;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
DB3D0FF225BAA61700EAA174 /* SwiftPackageProductDependency */,
|
DB3D0FF225BAA61700EAA174 /* AlamofireImage */,
|
||||||
5D526FE125BE9AC400460CB9 /* SwiftPackageProductDependency */,
|
5D526FE125BE9AC400460CB9 /* MastodonSDK */,
|
||||||
2D61336825C18A4F00CAE157 /* SwiftPackageProductDependency */,
|
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */,
|
||||||
2D42FF6025C8177C004A627A /* SwiftPackageProductDependency */,
|
2D42FF6025C8177C004A627A /* ActiveLabel */,
|
||||||
DB0140BC25C40D7500F9F3CF /* SwiftPackageProductDependency */,
|
DB0140BC25C40D7500F9F3CF /* CommonOSLog */,
|
||||||
);
|
);
|
||||||
productName = Mastodon;
|
productName = Mastodon;
|
||||||
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
||||||
|
@ -788,10 +809,10 @@
|
||||||
);
|
);
|
||||||
mainGroup = DB427DC925BAA00100D1B89D;
|
mainGroup = DB427DC925BAA00100D1B89D;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
DB3D0FF125BAA61700EAA174 /* RemoteSwiftPackageReference */,
|
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */,
|
||||||
2D61336725C18A4F00CAE157 /* RemoteSwiftPackageReference */,
|
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */,
|
||||||
2D42FF5F25C8177C004A627A /* RemoteSwiftPackageReference */,
|
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */,
|
||||||
DB0140BB25C40D7500F9F3CF /* RemoteSwiftPackageReference */,
|
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */,
|
||||||
);
|
);
|
||||||
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
@ -973,6 +994,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */,
|
||||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
||||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
||||||
|
@ -980,7 +1002,9 @@
|
||||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||||
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
||||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||||
|
DB98338825C945ED00AD9700 /* Assets.swift in Sources */,
|
||||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||||
|
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */,
|
||||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
||||||
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */,
|
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */,
|
||||||
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */,
|
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */,
|
||||||
|
@ -995,18 +1019,19 @@
|
||||||
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */,
|
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */,
|
||||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
|
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
|
||||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
||||||
|
DB98338725C945ED00AD9700 /* Strings.swift in Sources */,
|
||||||
|
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */,
|
||||||
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
||||||
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */,
|
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */,
|
||||||
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */,
|
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */,
|
||||||
|
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */,
|
||||||
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */,
|
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */,
|
||||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
||||||
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
||||||
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */,
|
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */,
|
||||||
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */,
|
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */,
|
||||||
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */,
|
|
||||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
||||||
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
|
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
|
||||||
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */,
|
|
||||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
||||||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -1518,7 +1543,7 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
2D42FF5F25C8177C004A627A /* RemoteSwiftPackageReference */ = {
|
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/TwidereProject/ActiveLabel.swift";
|
repositoryURL = "https://github.com/TwidereProject/ActiveLabel.swift";
|
||||||
requirement = {
|
requirement = {
|
||||||
|
@ -1526,7 +1551,7 @@
|
||||||
minimumVersion = 3.0.0;
|
minimumVersion = 3.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
2D61336725C18A4F00CAE157 /* RemoteSwiftPackageReference */ = {
|
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/Alamofire/AlamofireNetworkActivityIndicator";
|
repositoryURL = "https://github.com/Alamofire/AlamofireNetworkActivityIndicator";
|
||||||
requirement = {
|
requirement = {
|
||||||
|
@ -1534,7 +1559,7 @@
|
||||||
minimumVersion = 3.1.0;
|
minimumVersion = 3.1.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
DB0140BB25C40D7500F9F3CF /* RemoteSwiftPackageReference */ = {
|
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/MainasuK/CommonOSLog";
|
repositoryURL = "https://github.com/MainasuK/CommonOSLog";
|
||||||
requirement = {
|
requirement = {
|
||||||
|
@ -1542,7 +1567,7 @@
|
||||||
minimumVersion = 0.1.1;
|
minimumVersion = 0.1.1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
DB3D0FF125BAA61700EAA174 /* RemoteSwiftPackageReference */ = {
|
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/Alamofire/AlamofireImage.git";
|
repositoryURL = "https://github.com/Alamofire/AlamofireImage.git";
|
||||||
requirement = {
|
requirement = {
|
||||||
|
@ -1553,28 +1578,28 @@
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
2D42FF6025C8177C004A627A /* SwiftPackageProductDependency */ = {
|
2D42FF6025C8177C004A627A /* ActiveLabel */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 2D42FF5F25C8177C004A627A /* RemoteSwiftPackageReference */;
|
package = 2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */;
|
||||||
productName = ActiveLabel;
|
productName = ActiveLabel;
|
||||||
};
|
};
|
||||||
2D61336825C18A4F00CAE157 /* SwiftPackageProductDependency */ = {
|
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 2D61336725C18A4F00CAE157 /* RemoteSwiftPackageReference */;
|
package = 2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */;
|
||||||
productName = AlamofireNetworkActivityIndicator;
|
productName = AlamofireNetworkActivityIndicator;
|
||||||
};
|
};
|
||||||
5D526FE125BE9AC400460CB9 /* SwiftPackageProductDependency */ = {
|
5D526FE125BE9AC400460CB9 /* MastodonSDK */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = MastodonSDK;
|
productName = MastodonSDK;
|
||||||
};
|
};
|
||||||
DB0140BC25C40D7500F9F3CF /* SwiftPackageProductDependency */ = {
|
DB0140BC25C40D7500F9F3CF /* CommonOSLog */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = DB0140BB25C40D7500F9F3CF /* RemoteSwiftPackageReference */;
|
package = DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */;
|
||||||
productName = CommonOSLog;
|
productName = CommonOSLog;
|
||||||
};
|
};
|
||||||
DB3D0FF225BAA61700EAA174 /* SwiftPackageProductDependency */ = {
|
DB3D0FF225BAA61700EAA174 /* AlamofireImage */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = DB3D0FF125BAA61700EAA174 /* RemoteSwiftPackageReference */;
|
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
||||||
productName = AlamofireImage;
|
productName = AlamofireImage;
|
||||||
};
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
|
|
@ -38,6 +38,7 @@ extension SceneCoordinator {
|
||||||
|
|
||||||
enum Scene {
|
enum Scene {
|
||||||
case authentication(viewModel: AuthenticationViewModel)
|
case authentication(viewModel: AuthenticationViewModel)
|
||||||
|
case mastodonPinBasedAuthentication(viewModel: MastodonPinBasedAuthenticationViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +114,10 @@ private extension SceneCoordinator {
|
||||||
let _viewController = AuthenticationViewController()
|
let _viewController = AuthenticationViewController()
|
||||||
_viewController.viewModel = viewModel
|
_viewController.viewModel = viewModel
|
||||||
viewController = _viewController
|
viewController = _viewController
|
||||||
|
case .mastodonPinBasedAuthentication(let viewModel):
|
||||||
|
let _viewController = MastodonPinBasedAuthenticationViewController()
|
||||||
|
_viewController.viewModel = viewModel
|
||||||
|
viewController = _viewController
|
||||||
}
|
}
|
||||||
|
|
||||||
setupDependency(for: viewController as? NeedsDependency)
|
setupDependency(for: viewController as? NeedsDependency)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
final class AuthenticationViewController: UIViewController, NeedsDependency {
|
final class AuthenticationViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ final class AuthenticationViewController: UIViewController, NeedsDependency {
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
var viewModel: AuthenticationViewModel!
|
var viewModel: AuthenticationViewModel!
|
||||||
|
var mastodonPinBasedAuthenticationViewController: UIViewController?
|
||||||
|
|
||||||
let domainTextField: UITextField = {
|
let domainTextField: UITextField = {
|
||||||
let textField = UITextField()
|
let textField = UITextField()
|
||||||
|
@ -75,6 +77,18 @@ extension AuthenticationViewController {
|
||||||
|
|
||||||
@objc private func signInBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
@objc private func signInBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
guard let domain = viewModel.domain.value else {
|
||||||
|
// TODO: alert error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewModel.signInAction.send(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||||
|
extension AuthenticationViewController: UIAdaptivePresentationControllerDelegate {
|
||||||
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||||
|
return .fullScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,21 +5,34 @@
|
||||||
// Created by MainasuK Cirno on 2021/2/1.
|
// Created by MainasuK Cirno on 2021/2/1.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import os.log
|
||||||
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
final class AuthenticationViewModel {
|
final class AuthenticationViewModel {
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
let context: AppContext
|
||||||
|
let coordinator: SceneCoordinator
|
||||||
let input = CurrentValueSubject<String, Never>("")
|
let input = CurrentValueSubject<String, Never>("")
|
||||||
|
let signInAction = PassthroughSubject<String, Never>()
|
||||||
|
|
||||||
// output
|
// output
|
||||||
let domain = CurrentValueSubject<String?, Never>(nil)
|
let domain = CurrentValueSubject<String?, Never>(nil)
|
||||||
let isSignInButtonEnabled = CurrentValueSubject<Bool, Never>(false)
|
let isSignInButtonEnabled = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
let isAuthenticating = CurrentValueSubject<Bool, Never>(false)
|
||||||
|
let authenticated = PassthroughSubject<Void, Never>()
|
||||||
|
let error = CurrentValueSubject<Error?, Never>(nil)
|
||||||
|
|
||||||
init() {
|
private var mastodonPinBasedAuthenticationViewController: UIViewController?
|
||||||
|
|
||||||
|
init(context: AppContext, coordinator: SceneCoordinator) {
|
||||||
|
self.context = context
|
||||||
|
self.coordinator = coordinator
|
||||||
|
|
||||||
input
|
input
|
||||||
.map { input in
|
.map { input in
|
||||||
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||||
|
@ -39,10 +52,135 @@ final class AuthenticationViewModel {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
domain
|
domain
|
||||||
.print()
|
|
||||||
.map { $0 != nil }
|
.map { $0 != nil }
|
||||||
.assign(to: \.value, on: isSignInButtonEnabled)
|
.assign(to: \.value, on: isSignInButtonEnabled)
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
signInAction
|
||||||
|
.handleEvents(receiveOutput: { [weak self] _ in
|
||||||
|
// trigger state change
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.isAuthenticating.value = true
|
||||||
|
})
|
||||||
|
.flatMap { domain in
|
||||||
|
context.apiService.createApplication(domain: domain)
|
||||||
|
.retry(3)
|
||||||
|
.tryMap { response -> AuthenticateInfo in
|
||||||
|
let application = response.value
|
||||||
|
guard let clientID = application.clientID,
|
||||||
|
let clientSecret = application.clientSecret else {
|
||||||
|
throw APIService.APIError.explicit(.badResponse)
|
||||||
|
}
|
||||||
|
let query = Mastodon.API.OAuth.AuthorizeQuery(clientID: clientID)
|
||||||
|
let url = Mastodon.API.OAuth.authorizeURL(domain: domain, query: query)
|
||||||
|
return AuthenticateInfo(
|
||||||
|
domain: domain,
|
||||||
|
clientID: clientID,
|
||||||
|
clientSecret: clientSecret,
|
||||||
|
url: url
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
guard let self = self else { return }
|
||||||
|
// trigger state update
|
||||||
|
self.isAuthenticating.value = false
|
||||||
|
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
// TODO: handle error
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign in fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
|
self.error.value = error
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] info in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let mastodonPinBasedAuthenticationViewModel = MastodonPinBasedAuthenticationViewModel(authenticateURL: info.url)
|
||||||
|
self.authenticate(
|
||||||
|
info: info,
|
||||||
|
pinCodePublisher: mastodonPinBasedAuthenticationViewModel.pinCodePublisher
|
||||||
|
)
|
||||||
|
self.mastodonPinBasedAuthenticationViewController = self.coordinator.present(
|
||||||
|
scene: .mastodonPinBasedAuthentication(viewModel: mastodonPinBasedAuthenticationViewModel),
|
||||||
|
from: nil,
|
||||||
|
transition: .modal(animated: true, completion: nil)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AuthenticationViewModel {
|
||||||
|
|
||||||
|
struct AuthenticateInfo {
|
||||||
|
let domain: String
|
||||||
|
let clientID: String
|
||||||
|
let clientSecret: String
|
||||||
|
let url: URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticate(info: AuthenticateInfo, pinCodePublisher: PassthroughSubject<String, Never>) {
|
||||||
|
pinCodePublisher
|
||||||
|
.handleEvents(receiveOutput: { [weak self] _ in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.isAuthenticating.value = true
|
||||||
|
self.mastodonPinBasedAuthenticationViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
self.mastodonPinBasedAuthenticationViewController = nil
|
||||||
|
})
|
||||||
|
.compactMap { [weak self] code -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error>? in
|
||||||
|
guard let self = self else { return nil }
|
||||||
|
return self.context.apiService
|
||||||
|
.userAccessToken(
|
||||||
|
domain: info.domain,
|
||||||
|
clientID: info.clientID,
|
||||||
|
clientSecret: info.clientSecret,
|
||||||
|
code: code
|
||||||
|
)
|
||||||
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> in
|
||||||
|
let token = response.value
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign in success. Token: %s", ((#file as NSString).lastPathComponent), #line, #function, token.accessToken)
|
||||||
|
return AuthenticationViewModel.verifyAndSaveAuthentication(
|
||||||
|
context: self.context,
|
||||||
|
info: info,
|
||||||
|
token: token
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
.switchToLatest()
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] completion in
|
||||||
|
guard let self = self else { return }
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: swap user access token swap fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
|
self.isAuthenticating.value = false
|
||||||
|
self.error.value = error
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let account = response.value
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
.store(in: &self.disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func verifyAndSaveAuthentication(
|
||||||
|
context: AppContext,
|
||||||
|
info: AuthenticateInfo,
|
||||||
|
token: Mastodon.Entity.Token
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||||
|
let authorization = Mastodon.API.OAuth.Authorization(accessToken: token.accessToken)
|
||||||
|
return context.apiService.accountVerifyCredentials(
|
||||||
|
domain: info.domain,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
// TODO: add persist logic
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,20 +6,70 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import Foundation
|
import UIKit
|
||||||
|
import Combine
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
final class MastodonPinBasedAuthenticationViewController: NSObject {
|
final class MastodonPinBasedAuthenticationViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
weak var viewModel: MastodonPinBasedAuthenticationViewModel?
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
var viewModel: MastodonPinBasedAuthenticationViewModel!
|
||||||
|
|
||||||
init(viewModel: MastodonPinBasedAuthenticationViewModel) {
|
let webView: WKWebView = {
|
||||||
self.viewModel = viewModel
|
let configuration = WKWebViewConfiguration()
|
||||||
}
|
configuration.processPool = WKProcessPool()
|
||||||
|
let webView = WKWebView(frame: .zero, configuration: configuration)
|
||||||
|
return webView
|
||||||
|
}()
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
|
||||||
|
// cleanup cookie
|
||||||
|
let httpCookieStore = webView.configuration.websiteDataStore.httpCookieStore
|
||||||
|
httpCookieStore.getAllCookies { cookies in
|
||||||
|
for cookie in cookies {
|
||||||
|
httpCookieStore.delete(cookie, completionHandler: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extension MastodonPinBasedAuthenticationViewController {
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
title = "Authentication"
|
||||||
|
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(MastodonPinBasedAuthenticationViewController.cancelBarButtonItemPressed(_:)))
|
||||||
|
|
||||||
|
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
view.addSubview(webView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
webView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
|
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
|
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
let request = URLRequest(url: viewModel.authenticateURL)
|
||||||
|
webView.navigationDelegate = viewModel.navigationDelegate
|
||||||
|
webView.load(request)
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: authenticate via: %s", ((#file as NSString).lastPathComponent), #line, #function, viewModel.authenticateURL.debugDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MastodonPinBasedAuthenticationViewController {
|
||||||
|
|
||||||
|
@objc private func cancelBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
|
dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,36 @@
|
||||||
// Created by MainasuK Cirno on 2021/1/29.
|
// Created by MainasuK Cirno on 2021/1/29.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import WebKit
|
||||||
|
|
||||||
final class MastodonPinBasedAuthenticationViewModel {
|
final class MastodonPinBasedAuthenticationViewModel {
|
||||||
|
|
||||||
|
// input
|
||||||
|
let authenticateURL: URL
|
||||||
|
|
||||||
|
// output
|
||||||
|
let pinCodePublisher = PassthroughSubject<String, Never>()
|
||||||
|
private var navigationDelegateShim: MastodonPinBasedAuthenticationViewModelNavigationDelegateShim?
|
||||||
|
|
||||||
|
init(authenticateURL: URL) {
|
||||||
|
self.authenticateURL = authenticateURL
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MastodonPinBasedAuthenticationViewModel {
|
||||||
|
|
||||||
|
var navigationDelegate: WKNavigationDelegate {
|
||||||
|
let navigationDelegateShim = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim(viewModel: self)
|
||||||
|
self.navigationDelegateShim = navigationDelegateShim
|
||||||
|
return navigationDelegateShim
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,14 @@ final class MastodonPinBasedAuthenticationViewModelNavigationDelegateShim: NSObj
|
||||||
extension MastodonPinBasedAuthenticationViewModelNavigationDelegateShim: WKNavigationDelegate {
|
extension MastodonPinBasedAuthenticationViewModelNavigationDelegateShim: WKNavigationDelegate {
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
// TODO:
|
guard let url = webView.url,
|
||||||
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||||
|
let codeQueryItem = components.queryItems?.first(where: { $0.name == "code" }),
|
||||||
|
let code = codeQueryItem.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel?.pinCodePublisher.send(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// APIService+Error.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by Cirno MainasuK on 2021-2-2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
enum APIError: Error {
|
||||||
|
|
||||||
|
case implicit(ErrorReason)
|
||||||
|
case explicit(ErrorReason)
|
||||||
|
|
||||||
|
enum ErrorReason {
|
||||||
|
// application internal error
|
||||||
|
case authenticationMissing
|
||||||
|
case badRequest
|
||||||
|
case badResponse
|
||||||
|
case requestThrottle
|
||||||
|
|
||||||
|
// Server API error
|
||||||
|
case mastodonAPIError(Mastodon.API.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// APIService+Account.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
|
||||||
|
func accountVerifyCredentials(
|
||||||
|
domain: String,
|
||||||
|
authorization: Mastodon.API.OAuth.Authorization
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||||
|
return Mastodon.API.Account.verifyCredentials(
|
||||||
|
session: session,
|
||||||
|
domain: domain,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// APIService+App.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private static let clientName = "Skimming"
|
||||||
|
#else
|
||||||
|
private static let clientName = "Mastodon for iOS"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
func createApplication(domain: String) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Application>, Error> {
|
||||||
|
let query = Mastodon.API.App.CreateQuery(clientName: APIService.clientName, website: nil)
|
||||||
|
return Mastodon.API.App.create(
|
||||||
|
session: session,
|
||||||
|
domain: domain,
|
||||||
|
query: query
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// APIService+Authentication.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
|
||||||
|
func userAccessToken(
|
||||||
|
domain: String,
|
||||||
|
clientID: String,
|
||||||
|
clientSecret: String,
|
||||||
|
code: String
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
||||||
|
let query = Mastodon.API.OAuth.AccessTokenQuery(
|
||||||
|
clientID: clientID,
|
||||||
|
clientSecret: clientSecret,
|
||||||
|
code: code,
|
||||||
|
grantType: "authorization_code"
|
||||||
|
)
|
||||||
|
return Mastodon.API.OAuth.accessToken(
|
||||||
|
session: session,
|
||||||
|
domain: domain,
|
||||||
|
query: query
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ extension APIService {
|
||||||
domain: domain,
|
domain: domain,
|
||||||
query: Mastodon.API.Timeline.PublicTimelineQuery()
|
query: Mastodon.API.Timeline.PublicTimelineQuery()
|
||||||
)
|
)
|
||||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>,Error> in
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>, Error> in
|
||||||
return APIService.Persist.persistTimeline(
|
return APIService.Persist.persistTimeline(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
managedObjectContext: self.backgroundManagedObjectContext,
|
managedObjectContext: self.backgroundManagedObjectContext,
|
||||||
|
@ -46,4 +46,5 @@ extension APIService {
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let authenticationViewModel = AuthenticationViewModel()
|
let authenticationViewModel = AuthenticationViewModel(context: appContext, coordinator: sceneCoordinator)
|
||||||
sceneCoordinator.present(scene: .authentication(viewModel: authenticationViewModel), from: nil, transition: .modal(animated: false, completion: nil))
|
sceneCoordinator.present(scene: .authentication(viewModel: authenticationViewModel), from: nil, transition: .modal(animated: false, completion: nil))
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// Mastodon+API+Account.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
extension Mastodon.API.Account {
|
||||||
|
|
||||||
|
static func verifyCredentialsEndpointURL(domain: String) -> URL {
|
||||||
|
return Mastodon.API.endpointURL(domain: domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func verifyCredentials(
|
||||||
|
session: URLSession,
|
||||||
|
domain: String,
|
||||||
|
authorization: Mastodon.API.OAuth.Authorization
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||||
|
let request = Mastodon.API.get(
|
||||||
|
url: verifyCredentialsEndpointURL(domain: domain),
|
||||||
|
query: nil,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
return session.dataTaskPublisher(for: request)
|
||||||
|
.tryMap { data, response in
|
||||||
|
let value = try Mastodon.API.decode(type: Mastodon.Entity.Account.self, from: data, response: response)
|
||||||
|
return Mastodon.Response.Content(value: value, response: response)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,10 @@ extension Mastodon.API.OAuth {
|
||||||
|
|
||||||
public struct Authorization {
|
public struct Authorization {
|
||||||
public let accessToken: String
|
public let accessToken: String
|
||||||
|
|
||||||
|
public init(accessToken: String) {
|
||||||
|
self.accessToken = accessToken
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +27,9 @@ extension Mastodon.API.OAuth {
|
||||||
static func authorizeEndpointURL(domain: String) -> URL {
|
static func authorizeEndpointURL(domain: String) -> URL {
|
||||||
return Mastodon.API.oauthEndpointURL(domain: domain).appendingPathComponent("authorize")
|
return Mastodon.API.oauthEndpointURL(domain: domain).appendingPathComponent("authorize")
|
||||||
}
|
}
|
||||||
|
static func accessTokenEndpointURL(domain: String) -> URL {
|
||||||
|
return Mastodon.API.oauthEndpointURL(domain: domain).appendingPathComponent("token")
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct user authorize endpoint URL
|
/// Construct user authorize endpoint URL
|
||||||
///
|
///
|
||||||
|
@ -38,7 +45,7 @@ extension Mastodon.API.OAuth {
|
||||||
/// - session: `URLSession`
|
/// - session: `URLSession`
|
||||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
/// - query: `AuthorizeQuery`
|
/// - query: `AuthorizeQuery`
|
||||||
static func authorizeURL(
|
public static func authorizeURL(
|
||||||
domain: String,
|
domain: String,
|
||||||
query: AuthorizeQuery
|
query: AuthorizeQuery
|
||||||
) -> URL {
|
) -> URL {
|
||||||
|
@ -51,27 +58,41 @@ extension Mastodon.API.OAuth {
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
// static func authorize(
|
/// Obtain User Access Token
|
||||||
// session: URLSession,
|
///
|
||||||
// domain: String,
|
/// - Since: 0.0.0
|
||||||
// query: AuthorizeQuery
|
/// - Version: 3.3.0
|
||||||
// ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
/// # Last Update
|
||||||
// let request = Mastodon.API.post(
|
/// 2021/2/2
|
||||||
// url: authorizeEndpointURL(domain: domain),
|
/// # Reference
|
||||||
// query: query,
|
/// [Document](https://docs.joinmastodon.org/methods/apps/oauth/)
|
||||||
// authorization: nil
|
/// - Parameters:
|
||||||
// )
|
/// - session: `URLSession`
|
||||||
// return session.dataTaskPublisher(for: request)
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
// .tryMap { data, response in
|
/// - query: `AccessTokenQuery`
|
||||||
// let value = try Mastodon.API.decode(type: Mastodon.Entity.Token.self, from: data, response: response)
|
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||||
// return Mastodon.Response.Content(value: value, response: response)
|
public static func accessToken(
|
||||||
// }
|
session: URLSession,
|
||||||
// .eraseToAnyPublisher()
|
domain: String,
|
||||||
// }
|
query: AccessTokenQuery
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
||||||
|
let request = Mastodon.API.post(
|
||||||
|
url: accessTokenEndpointURL(domain: domain),
|
||||||
|
query: query,
|
||||||
|
authorization: nil
|
||||||
|
)
|
||||||
|
return session.dataTaskPublisher(for: request)
|
||||||
|
.tryMap { data, response in
|
||||||
|
let value = try Mastodon.API.decode(type: Mastodon.Entity.Token.self, from: data, response: response)
|
||||||
|
return Mastodon.Response.Content(value: value, response: response)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API.OAuth {
|
extension Mastodon.API.OAuth {
|
||||||
|
|
||||||
public struct AuthorizeQuery: GetQuery {
|
public struct AuthorizeQuery: GetQuery {
|
||||||
|
|
||||||
public let forceLogin: String?
|
public let forceLogin: String?
|
||||||
|
@ -106,7 +127,7 @@ extension Mastodon.API.OAuth {
|
||||||
var items: [URLQueryItem] = []
|
var items: [URLQueryItem] = []
|
||||||
forceLogin.flatMap { items.append(URLQueryItem(name: "force_login", value: $0)) }
|
forceLogin.flatMap { items.append(URLQueryItem(name: "force_login", value: $0)) }
|
||||||
items.append(URLQueryItem(name: "response_type", value: responseType))
|
items.append(URLQueryItem(name: "response_type", value: responseType))
|
||||||
items.append(URLQueryItem(name: "clientID", value: clientID))
|
items.append(URLQueryItem(name: "client_id", value: clientID))
|
||||||
items.append(URLQueryItem(name: "redirect_uri", value: redirectURI))
|
items.append(URLQueryItem(name: "redirect_uri", value: redirectURI))
|
||||||
scope.flatMap { items.append(URLQueryItem(name: "scope", value: $0)) }
|
scope.flatMap { items.append(URLQueryItem(name: "scope", value: $0)) }
|
||||||
guard !items.isEmpty else { return nil }
|
guard !items.isEmpty else { return nil }
|
||||||
|
@ -114,4 +135,46 @@ extension Mastodon.API.OAuth {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct AccessTokenQuery: Codable, PostQuery {
|
||||||
|
public init(
|
||||||
|
clientID: String,
|
||||||
|
clientSecret: String,
|
||||||
|
redirectURI: String = "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
scope: String? = "read write follow push",
|
||||||
|
code: String?,
|
||||||
|
grantType: String
|
||||||
|
) {
|
||||||
|
self.clientID = clientID
|
||||||
|
self.clientSecret = clientSecret
|
||||||
|
self.redirectURI = redirectURI
|
||||||
|
self.scope = scope
|
||||||
|
self.code = code
|
||||||
|
self.grantType = grantType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public let clientID: String
|
||||||
|
public let clientSecret: String
|
||||||
|
public let redirectURI: String
|
||||||
|
public let scope: String?
|
||||||
|
public let code: String?
|
||||||
|
public let grantType: String
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case clientID = "client_id"
|
||||||
|
case clientSecret = "client_secret"
|
||||||
|
case redirectURI = "redirect_uri"
|
||||||
|
case scope
|
||||||
|
case code
|
||||||
|
case grantType = "grant_type"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: Data? {
|
||||||
|
return try? Mastodon.API.encoder.encode(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,17 +37,33 @@ extension Mastodon.API {
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
decoder.dateDecodingStrategy = JSONDecoder.DateDecodingStrategy.custom { decoder throws -> Date in
|
decoder.dateDecodingStrategy = JSONDecoder.DateDecodingStrategy.custom { decoder throws -> Date in
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let string = try container.decode(String.self)
|
|
||||||
|
|
||||||
|
var logInfo = ""
|
||||||
if let date = fractionalSecondsPreciseISO8601Formatter.date(from: string) {
|
do {
|
||||||
return date
|
let string = try container.decode(String.self)
|
||||||
}
|
logInfo += string
|
||||||
if let date = fullDatePreciseISO8601Formatter.date(from: string) {
|
|
||||||
return date
|
if let date = fractionalSecondsPreciseISO8601Formatter.date(from: string) {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
if let date = fullDatePreciseISO8601Formatter.date(from: string) {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
|
var numberValue = ""
|
||||||
|
do {
|
||||||
|
let number = try container.decode(Double.self)
|
||||||
|
logInfo += "\(number)"
|
||||||
|
|
||||||
|
return Date(timeIntervalSince1970: number)
|
||||||
|
} catch {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
throw DecodingError.dataCorruptedError(in: container, debugDescription: "[Decoder] Invalid date: \(logInfo)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoder
|
return decoder
|
||||||
|
@ -66,6 +82,7 @@ extension Mastodon.API {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API {
|
extension Mastodon.API {
|
||||||
|
public enum Account { }
|
||||||
public enum App { }
|
public enum App { }
|
||||||
public enum OAuth { }
|
public enum OAuth { }
|
||||||
public enum Timeline { }
|
public enum Timeline { }
|
||||||
|
|
|
@ -21,5 +21,12 @@ extension Mastodon.Entity {
|
||||||
public let tokenType: String
|
public let tokenType: String
|
||||||
public let scope: String
|
public let scope: String
|
||||||
public let createdAt: Date
|
public let createdAt: Date
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case accessToken = "access_token"
|
||||||
|
case tokenType = "token_type"
|
||||||
|
case scope
|
||||||
|
case createdAt = "created_at"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue