mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-16 18:23:53 +01:00
Add onboarding screens for an easy setup of SideStore
This commit is contained in:
@@ -63,6 +63,10 @@
|
|||||||
1F943C6F2927F90400ABE095 /* NewsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5B82927E0EE00B8D837 /* NewsView.swift */; };
|
1F943C6F2927F90400ABE095 /* NewsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5B82927E0EE00B8D837 /* NewsView.swift */; };
|
||||||
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BA2927E0F800B8D837 /* BrowseView.swift */; };
|
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BA2927E0F800B8D837 /* BrowseView.swift */; };
|
||||||
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BD2927E10D00B8D837 /* MyAppsView.swift */; };
|
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BD2927E10D00B8D837 /* MyAppsView.swift */; };
|
||||||
|
1F981B1129AA0FAE0014950E /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F981B1029AA0FAE0014950E /* OnboardingView.swift */; };
|
||||||
|
1F981B1329AA101F0014950E /* OnboardingStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F981B1229AA101F0014950E /* OnboardingStepView.swift */; };
|
||||||
|
1F981B1529AA1E070014950E /* AppIconsShowcase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F981B1429AA1E070014950E /* AppIconsShowcase.swift */; };
|
||||||
|
1F981B1729AA34A70014950E /* AppStoreProductView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F981B1629AA34A70014950E /* AppStoreProductView.swift */; };
|
||||||
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */; };
|
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */; };
|
||||||
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */; };
|
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */; };
|
||||||
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */; };
|
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */; };
|
||||||
@@ -76,6 +80,8 @@
|
|||||||
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */; };
|
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */; };
|
||||||
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FEB292C171D007E68D1 /* NotificationManager.swift */; };
|
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FEB292C171D007E68D1 /* NotificationManager.swift */; };
|
||||||
1FB96FF3292D0539007E68D1 /* PillButtonProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */; };
|
1FB96FF3292D0539007E68D1 /* PillButtonProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */; };
|
||||||
|
1FF8C6182A1780C60041352C /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8C6172A1780C60041352C /* ActivityView.swift */; };
|
||||||
|
1FF8C61B2A1782F10041352C /* Reachability in Frameworks */ = {isa = PBXBuildFile; productRef = 1FF8C61A2A1782F10041352C /* Reachability */; };
|
||||||
1FFA56C2299994390011B6F5 /* OutputCapturer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFA56C1299994390011B6F5 /* OutputCapturer.swift */; };
|
1FFA56C2299994390011B6F5 /* OutputCapturer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFA56C1299994390011B6F5 /* OutputCapturer.swift */; };
|
||||||
1FFA56C52999978C0011B6F5 /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 1FFA56C42999978C0011B6F5 /* LocalConsole */; };
|
1FFA56C52999978C0011B6F5 /* LocalConsole in Frameworks */ = {isa = PBXBuildFile; productRef = 1FFA56C42999978C0011B6F5 /* LocalConsole */; };
|
||||||
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */; };
|
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */; };
|
||||||
@@ -609,6 +615,10 @@
|
|||||||
1F943C632927EF4200ABE095 /* NewsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsItemView.swift; sourceTree = "<group>"; };
|
1F943C632927EF4200ABE095 /* NewsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsItemView.swift; sourceTree = "<group>"; };
|
||||||
1F943C652927F36600ABE095 /* NewsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewModel.swift; sourceTree = "<group>"; };
|
1F943C652927F36600ABE095 /* NewsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewModel.swift; sourceTree = "<group>"; };
|
||||||
1F943C672927F39400ABE095 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
|
1F943C672927F39400ABE095 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
1F981B1029AA0FAE0014950E /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
||||||
|
1F981B1229AA101F0014950E /* OnboardingStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStepView.swift; sourceTree = "<group>"; };
|
||||||
|
1F981B1429AA1E070014950E /* AppIconsShowcase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconsShowcase.swift; sourceTree = "<group>"; };
|
||||||
|
1F981B1629AA34A70014950E /* AppStoreProductView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreProductView.swift; sourceTree = "<group>"; };
|
||||||
1FA1C8C9294906890083119D /* MyAppsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewModel.swift; sourceTree = "<group>"; };
|
1FA1C8C9294906890083119D /* MyAppsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewModel.swift; sourceTree = "<group>"; };
|
||||||
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsView.swift; sourceTree = "<group>"; };
|
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsView.swift; sourceTree = "<group>"; };
|
||||||
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposeView.swift; sourceTree = "<group>"; };
|
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposeView.swift; sourceTree = "<group>"; };
|
||||||
@@ -629,6 +639,7 @@
|
|||||||
1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriShortcutSetupView.swift; sourceTree = "<group>"; };
|
1FB96FCE292BBBC9007E68D1 /* SiriShortcutSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiriShortcutSetupView.swift; sourceTree = "<group>"; };
|
||||||
1FB96FEB292C171D007E68D1 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
1FB96FEB292C171D007E68D1 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; };
|
1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; };
|
||||||
|
1FF8C6172A1780C60041352C /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
|
||||||
1FFA56C1299994390011B6F5 /* OutputCapturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputCapturer.swift; sourceTree = "<group>"; };
|
1FFA56C1299994390011B6F5 /* OutputCapturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputCapturer.swift; sourceTree = "<group>"; };
|
||||||
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHistoryView.swift; sourceTree = "<group>"; };
|
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHistoryView.swift; sourceTree = "<group>"; };
|
||||||
9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; };
|
9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -1046,6 +1057,7 @@
|
|||||||
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
||||||
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */,
|
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */,
|
||||||
9922FFEC29B501C50020F868 /* Starscream in Frameworks */,
|
9922FFEC29B501C50020F868 /* Starscream in Frameworks */,
|
||||||
|
1FF8C61B2A1782F10041352C /* Reachability in Frameworks */,
|
||||||
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
||||||
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */,
|
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */,
|
||||||
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */,
|
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */,
|
||||||
@@ -1126,15 +1138,28 @@
|
|||||||
1F6284D6295218980060AAD8 /* DocumentPicker.swift */,
|
1F6284D6295218980060AAD8 /* DocumentPicker.swift */,
|
||||||
1F545E84298D84CF00589F68 /* FilePreviewView.swift */,
|
1F545E84298D84CF00589F68 /* FilePreviewView.swift */,
|
||||||
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */,
|
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */,
|
||||||
|
1F981B1629AA34A70014950E /* AppStoreProductView.swift */,
|
||||||
|
1FF8C6172A1780C60041352C /* ActivityView.swift */,
|
||||||
);
|
);
|
||||||
path = "UIView Representables";
|
path = "UIView Representables";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
1F981B0F29AA0F9B0014950E /* Onboarding */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1F981B1029AA0FAE0014950E /* OnboardingView.swift */,
|
||||||
|
1F981B1229AA101F0014950E /* OnboardingStepView.swift */,
|
||||||
|
1F981B1429AA1E070014950E /* AppIconsShowcase.swift */,
|
||||||
|
);
|
||||||
|
path = Onboarding;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1FAFC5B02927E01400B8D837 /* Views */ = {
|
1FAFC5B02927E01400B8D837 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1FAFC5B52927E06300B8D837 /* RootView.swift */,
|
1FAFC5B52927E06300B8D837 /* RootView.swift */,
|
||||||
1FAFC5B12927E02E00B8D837 /* Authentication */,
|
1FAFC5B12927E02E00B8D837 /* Authentication */,
|
||||||
|
1F981B0F29AA0F9B0014950E /* Onboarding */,
|
||||||
1FAFC5B22927E03300B8D837 /* News */,
|
1FAFC5B22927E03300B8D837 /* News */,
|
||||||
1FAFC5B32927E03D00B8D837 /* Browse */,
|
1FAFC5B32927E03D00B8D837 /* Browse */,
|
||||||
1FAFC5BC2927E0FD00B8D837 /* My Apps */,
|
1FAFC5BC2927E0FD00B8D837 /* My Apps */,
|
||||||
@@ -2312,6 +2337,7 @@
|
|||||||
1F07F5662955D16A00F7BE95 /* SFSafeSymbols */,
|
1F07F5662955D16A00F7BE95 /* SFSafeSymbols */,
|
||||||
1F1295802989B51F0048FCB9 /* ExpandableText */,
|
1F1295802989B51F0048FCB9 /* ExpandableText */,
|
||||||
1FFA56C42999978C0011B6F5 /* LocalConsole */,
|
1FFA56C42999978C0011B6F5 /* LocalConsole */,
|
||||||
|
1FF8C61A2A1782F10041352C /* Reachability */,
|
||||||
);
|
);
|
||||||
productName = AltStore;
|
productName = AltStore;
|
||||||
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
||||||
@@ -2389,6 +2415,7 @@
|
|||||||
1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
1F07F5652955D16A00F7BE95 /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
||||||
1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */,
|
1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */,
|
||||||
1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */,
|
1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */,
|
||||||
|
1FF8C6192A1782F10041352C /* XCRemoteSwiftPackageReference "Reachability" */,
|
||||||
);
|
);
|
||||||
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -2814,6 +2841,7 @@
|
|||||||
BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */,
|
BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */,
|
||||||
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
||||||
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */,
|
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */,
|
||||||
|
1F981B1529AA1E070014950E /* AppIconsShowcase.swift in Sources */,
|
||||||
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
||||||
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */,
|
1FFEF104298552DB0098374C /* AppVersionHistoryView.swift in Sources */,
|
||||||
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
||||||
@@ -2841,10 +2869,13 @@
|
|||||||
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */,
|
1FB96FEC292C171D007E68D1 /* NotificationManager.swift in Sources */,
|
||||||
BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */,
|
BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */,
|
||||||
1F07F56B2955F11500F7BE95 /* AppScreenshotsPreview.swift in Sources */,
|
1F07F56B2955F11500F7BE95 /* AppScreenshotsPreview.swift in Sources */,
|
||||||
|
1F981B1329AA101F0014950E /* OnboardingStepView.swift in Sources */,
|
||||||
BF44EEFC246B4550002A52F2 /* RemoveAppOperation.swift in Sources */,
|
BF44EEFC246B4550002A52F2 /* RemoveAppOperation.swift in Sources */,
|
||||||
BF3D64B022E8D4B800E9056B /* AppContentViewControllerCells.swift in Sources */,
|
BF3D64B022E8D4B800E9056B /* AppContentViewControllerCells.swift in Sources */,
|
||||||
|
1F981B1729AA34A70014950E /* AppStoreProductView.swift in Sources */,
|
||||||
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */,
|
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */,
|
||||||
B3EE16B62925E27D00B3B1F5 /* AnisetteManager.swift in Sources */,
|
B3EE16B62925E27D00B3B1F5 /* AnisetteManager.swift in Sources */,
|
||||||
|
1FF8C6182A1780C60041352C /* ActivityView.swift in Sources */,
|
||||||
1F943C6E2927F90400ABE095 /* NewsItemView.swift in Sources */,
|
1F943C6E2927F90400ABE095 /* NewsItemView.swift in Sources */,
|
||||||
BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */,
|
BF88F97224F8727D00BB75DF /* AppManagerErrors.swift in Sources */,
|
||||||
B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */,
|
B39F16152918D7DA002E9404 /* Consts+Proxy.swift in Sources */,
|
||||||
@@ -2917,6 +2948,7 @@
|
|||||||
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
|
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
|
||||||
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,
|
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,
|
||||||
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */,
|
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */,
|
||||||
|
1F981B1129AA0FAE0014950E /* OnboardingView.swift in Sources */,
|
||||||
1F943C692927F8F200ABE095 /* RootView.swift in Sources */,
|
1F943C692927F8F200ABE095 /* RootView.swift in Sources */,
|
||||||
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */,
|
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */,
|
||||||
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */,
|
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */,
|
||||||
@@ -3772,6 +3804,14 @@
|
|||||||
kind = branch;
|
kind = branch;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
1FF8C6192A1782F10041352C /* XCRemoteSwiftPackageReference "Reachability" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/ashleymills/Reachability.swift";
|
||||||
|
requirement = {
|
||||||
|
branch = master;
|
||||||
|
kind = branch;
|
||||||
|
};
|
||||||
|
};
|
||||||
1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */ = {
|
1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/duraidabdul/LocalConsole.git";
|
repositoryURL = "https://github.com/duraidabdul/LocalConsole.git";
|
||||||
@@ -3893,6 +3933,11 @@
|
|||||||
package = 1F74FF1C295263510047C051 /* XCRemoteSwiftPackageReference "AsyncImage" */;
|
package = 1F74FF1C295263510047C051 /* XCRemoteSwiftPackageReference "AsyncImage" */;
|
||||||
productName = AsyncImage;
|
productName = AsyncImage;
|
||||||
};
|
};
|
||||||
|
1FF8C61A2A1782F10041352C /* Reachability */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 1FF8C6192A1782F10041352C /* XCRemoteSwiftPackageReference "Reachability" */;
|
||||||
|
productName = Reachability;
|
||||||
|
};
|
||||||
1FFA56C42999978C0011B6F5 /* LocalConsole */ = {
|
1FFA56C42999978C0011B6F5 /* LocalConsole */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */;
|
package = 1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */;
|
||||||
|
|||||||
@@ -90,6 +90,15 @@
|
|||||||
"version" : "1.10.1"
|
"version" : "1.10.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "reachability.swift",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/ashleymills/Reachability.swift",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "master",
|
||||||
|
"revision" : "a81b7367f2c46875f29577e03a60c39cdfad0c8d"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "semanticversion",
|
"identity" : "semanticversion",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -54,15 +54,29 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
|
|||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(true)
|
super.viewDidAppear(true)
|
||||||
#if !targetEnvironment(simulator)
|
#if !targetEnvironment(simulator)
|
||||||
|
if !UserDefaults.standard.onboardingComplete {
|
||||||
|
self.showOnboarding()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
|
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
|
||||||
|
|
||||||
guard let pf = fetchPairingFile() else {
|
guard let pf = fetchPairingFile() else {
|
||||||
displayError("Device pairing file not found.")
|
self.showOnboarding(step: .pairing)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
start_minimuxer_threads(pf)
|
start_minimuxer_threads(pf)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showOnboarding(step: OnboardingView.OnboardingStep = .welcome) {
|
||||||
|
let onboardingView = OnboardingView(onDismiss: { self.dismiss(animated: true) }, currentStep: step)
|
||||||
|
.environment(\.managedObjectContext, DatabaseManager.shared.viewContext)
|
||||||
|
let navigationController = UINavigationController(rootViewController: UIHostingController(rootView: onboardingView))
|
||||||
|
navigationController.isNavigationBarHidden = true
|
||||||
|
navigationController.isModalInPresentation = true
|
||||||
|
self.present(navigationController, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
func fetchPairingFile() -> String? {
|
func fetchPairingFile() -> String? {
|
||||||
let filename = "ALTPairingFile.mobiledevicepairing"
|
let filename = "ALTPairingFile.mobiledevicepairing"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// ActivityView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 19.05.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
struct ActivityView: UIViewControllerRepresentable {
|
||||||
|
let items: [Any]
|
||||||
|
|
||||||
|
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||||
|
return UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// AppStoreProductView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 25.02.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
|
||||||
|
struct AppStoreView: UIViewControllerRepresentable {
|
||||||
|
typealias UIViewControllerType = AppStoreProductViewController
|
||||||
|
|
||||||
|
var isVisible: Binding<Bool>
|
||||||
|
let itunesItemId: Int
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> AppStoreProductViewController {
|
||||||
|
AppStoreProductViewController(isVisible: self.isVisible, itunesId: self.itunesItemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
|
||||||
|
if self.isVisible.wrappedValue {
|
||||||
|
uiViewController.presentStoreProduct()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AppStoreProductViewController: UIViewController {
|
||||||
|
|
||||||
|
private var isVisible: Binding<Bool>
|
||||||
|
private let itunesId: Int
|
||||||
|
|
||||||
|
init(isVisible: Binding<Bool>, itunesId: Int) {
|
||||||
|
self.isVisible = isVisible
|
||||||
|
self.itunesId = itunesId
|
||||||
|
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentStoreProduct() {
|
||||||
|
let storeProductViewController = SKStoreProductViewController()
|
||||||
|
storeProductViewController.delegate = self
|
||||||
|
|
||||||
|
let parameters = [SKStoreProductParameterITunesItemIdentifier: self.itunesId]
|
||||||
|
storeProductViewController.loadProduct(withParameters: parameters) { (success, error) -> Void in
|
||||||
|
if let error = error {
|
||||||
|
print("Failed to load App Store product: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
guard success else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.present(storeProductViewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - SKStoreProductViewControllerDelegate
|
||||||
|
|
||||||
|
extension AppStoreProductViewController: SKStoreProductViewControllerDelegate {
|
||||||
|
|
||||||
|
func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isVisible.wrappedValue = false
|
||||||
|
}
|
||||||
|
// viewController.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
89
AltStore/Views/Onboarding/AppIconsShowcase.swift
Normal file
89
AltStore/Views/Onboarding/AppIconsShowcase.swift
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// AppIconsShowcase.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 25.02.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct AppIconsShowcase: View {
|
||||||
|
|
||||||
|
@State var animationProgress = 0.0
|
||||||
|
@State var animation2Progress = 0.0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
GeometryReader { proxy in
|
||||||
|
ZStack(alignment: .bottom) {
|
||||||
|
Image(uiImage: UIImage(named: "AppIcon")!)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 0.2 * proxy.size.width)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
|
||||||
|
.offset(x: -0.3*proxy.size.width * self.animationProgress, y: -30)
|
||||||
|
.rotationEffect(.degrees(-20 * self.animationProgress))
|
||||||
|
.shadow(radius: 8 * self.animationProgress)
|
||||||
|
|
||||||
|
Image(uiImage: UIImage(named: "AppIcon")!)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 0.25 * proxy.size.width)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
|
||||||
|
.offset(x: -0.15*proxy.size.width * self.animationProgress, y: -10)
|
||||||
|
.rotationEffect(.degrees(-10 * self.animationProgress))
|
||||||
|
.shadow(radius: 12 * self.animationProgress)
|
||||||
|
|
||||||
|
Image(uiImage: UIImage(named: "AppIcon")!)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 0.2 * proxy.size.width)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
|
||||||
|
.offset(x: self.animationProgress*0.3*proxy.size.width, y: -30)
|
||||||
|
.rotationEffect(.degrees(self.animationProgress*20))
|
||||||
|
.shadow(radius: 8 * self.animationProgress)
|
||||||
|
|
||||||
|
Image(uiImage: UIImage(named: "AppIcon")!)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 0.25 * proxy.size.width)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
|
||||||
|
.offset(x: self.animationProgress * 0.15*proxy.size.width, y: -10)
|
||||||
|
.rotationEffect(.degrees(self.animationProgress * 10))
|
||||||
|
.shadow(radius: 12 * self.animationProgress)
|
||||||
|
|
||||||
|
Image(uiImage: UIImage(named: "AppIcon")!)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: 0.3 * proxy.size.width)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
|
||||||
|
.shadow(radius: 16 * self.animationProgress + 8 * self.animation2Progress)
|
||||||
|
.scaleEffect(1.0 + 0.05 * self.animation2Progress)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: proxy.size.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
|
withAnimation(.spring()) {
|
||||||
|
self.animationProgress = 1.0
|
||||||
|
self.animation2Progress = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||||
|
withAnimation(.spring()) {
|
||||||
|
self.animation2Progress = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppIconsShowcase_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
AppIconsShowcase()
|
||||||
|
.frame(height: 150)
|
||||||
|
}
|
||||||
|
}
|
||||||
137
AltStore/Views/Onboarding/OnboardingStepView.swift
Normal file
137
AltStore/Views/Onboarding/OnboardingStepView.swift
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
//
|
||||||
|
// OnboardingStepView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 25.02.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
|
struct OnboardingStep<Title: View, Hero: View, Content: View, Action: View> {
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var title: Title
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var hero: Hero
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var content: Content
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var action: Action
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct OnboardingStepView<Title: View, Hero: View, Content: View, Action: View>: View {
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var title: Title
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var hero: Hero
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var content: Content
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var action: Action
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 64) {
|
||||||
|
self.title
|
||||||
|
.font(.largeTitle.weight(.bold))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
self.hero
|
||||||
|
.frame(height: 150)
|
||||||
|
|
||||||
|
self.content
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
self.action
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnboardingStepView_Previews: PreviewProvider {
|
||||||
|
@State
|
||||||
|
static var isWireGuardAppStorePageVisible = false
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Welcome to")
|
||||||
|
Text("SideStore")
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
AppIconsShowcase()
|
||||||
|
}, content: {
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
Text("Before you can start sideloading apps, there is some setup to do.")
|
||||||
|
Text("The following setup will guide you through the steps one by one.")
|
||||||
|
Text("You will need a computer (Windows, macOS, Linux) and your Apple ID.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
SwiftUI.Button("Continue") {
|
||||||
|
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
})
|
||||||
|
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Pair your Device")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .link)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack {
|
||||||
|
Text("Before you can start sideloading apps, there is some setup to do.")
|
||||||
|
Text("The following setup will guide you through the steps one by one.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
SwiftUI.Button("Continue") {
|
||||||
|
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
})
|
||||||
|
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Download WireGuard")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .icloudAndArrowDown)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack {
|
||||||
|
Text("Before you can start sideloading apps, there is some setup to do.")
|
||||||
|
Text("The following setup will guide you through the steps one by one.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
SwiftUI.Button("Show in App Store") {
|
||||||
|
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
|
||||||
|
AppStoreView(isVisible: self.$isWireGuardAppStorePageVisible, itunesItemId: 1441195209)
|
||||||
|
.frame(width: .zero, height: .zero)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
444
AltStore/Views/Onboarding/OnboardingView.swift
Normal file
444
AltStore/Views/Onboarding/OnboardingView.swift
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
//
|
||||||
|
// OnboardingView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 25.02.23.
|
||||||
|
// Copyright © 2023 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import CoreData
|
||||||
|
import AltStoreCore
|
||||||
|
import minimuxer
|
||||||
|
import Reachability
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
|
||||||
|
struct OnboardingView: View {
|
||||||
|
enum OnboardingStep: Int {
|
||||||
|
case welcome, pairing, wireguard, wireguardConfig, addSources, finish
|
||||||
|
}
|
||||||
|
|
||||||
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
|
// Temporary workaround for UIKit compatibility
|
||||||
|
var onDismiss: (() -> Void)? = nil
|
||||||
|
|
||||||
|
@State var currentStep: OnboardingStep = .wireguard //.welcome
|
||||||
|
@State private var pairingFileURL: URL? = nil
|
||||||
|
@State private var isWireGuardAppStorePageVisible: Bool = false
|
||||||
|
@State private var isDownloadingWireGuardProfile: Bool = false
|
||||||
|
@State private var wireGuardProfileFileURL: URL? = nil
|
||||||
|
@State private var reachabilityNotifier: Reachability? = nil
|
||||||
|
@State private var isWireGuardTunnelReachable: Bool = false
|
||||||
|
@State private var areTrustedSourcesEnabled: Bool = false
|
||||||
|
@State private var isLoadingTrustedSources: Bool = false
|
||||||
|
|
||||||
|
let pairingFileTypes = UTType.types(tag: "plist", tagClass: UTTagClass.filenameExtension, conformingTo: nil) + UTType.types(tag: "mobiledevicepairing", tagClass: UTTagClass.filenameExtension, conformingTo: UTType.data) + [.xml]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
TabView(selection: self.$currentStep) {
|
||||||
|
welcomeStep
|
||||||
|
.tag(OnboardingStep.welcome)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
pairingView
|
||||||
|
.tag(OnboardingStep.pairing)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
wireguardView
|
||||||
|
.tag(OnboardingStep.wireguard)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
wireguardConfigView
|
||||||
|
.tag(OnboardingStep.wireguardConfig)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
addSourcesView
|
||||||
|
.tag(OnboardingStep.addSources)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
finishView
|
||||||
|
.tag(OnboardingStep.finish)
|
||||||
|
.highPriorityGesture(DragGesture())
|
||||||
|
|
||||||
|
}
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
|
.edgesIgnoringSafeArea(.bottom)
|
||||||
|
.background(Color.accentColor.opacity(0.1).edgesIgnoringSafeArea(.all))
|
||||||
|
.onChange(of: self.currentStep) { step in
|
||||||
|
switch step {
|
||||||
|
case .wireguardConfig:
|
||||||
|
self.startPingingWireGuardTunnel()
|
||||||
|
default:
|
||||||
|
self.stopPingingWireGuardTunnel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showNextStep() {
|
||||||
|
withAnimation {
|
||||||
|
self.currentStep = OnboardingStep(rawValue: self.currentStep.rawValue + 1) ?? self.currentStep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var welcomeStep: some View {
|
||||||
|
OnboardingStepView {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Welcome to")
|
||||||
|
Text("SideStore")
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
} hero: {
|
||||||
|
AppIconsShowcase()
|
||||||
|
} content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("Before you can start sideloading apps, there is some setup to do.")
|
||||||
|
Text("The following setup will guide you through the steps one by one.")
|
||||||
|
Text("You will need a computer (Windows, macOS, Linux) and your Apple ID.")
|
||||||
|
}
|
||||||
|
} action: {
|
||||||
|
SwiftUI.Button("Continue") {
|
||||||
|
self.showNextStep()
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pairingView: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Pair your Device")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .link)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("SideStore supports sideloading even on non-jailbroken devices.")
|
||||||
|
Text("For it to work, you have to generate a pairing file as described [here in our documentation](https://wiki.sidestore.io/guides/install#pairing-process).")
|
||||||
|
Text("Once you have the `<UUID>.mobiledevicepairing`, import it using the button below.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
ModalNavigationLink("Select Pairing File") {
|
||||||
|
DocumentPicker(selectedUrl: self.$pairingFileURL,
|
||||||
|
supportedTypes: self.pairingFileTypes.map { $0.identifier })
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
.onChange(of: self.pairingFileURL) { newValue in
|
||||||
|
guard let url = newValue else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.importPairingFile(url: url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var wireguardView: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Download WireGuard")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .icloudAndArrowDown)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("To sideload and sign app on-device without the need of a computer program like SideServer, a local WireGuard connection is required.")
|
||||||
|
Text("This connection is strictly local-only and does not connect to a server on the internet.")
|
||||||
|
Text("First, download WireGuard from the App Store (free).")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
AppStoreView(isVisible: self.$isWireGuardAppStorePageVisible, itunesItemId: 1441195209)
|
||||||
|
.frame(width: .zero, height: .zero)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
SwiftUI.Button("Show in App Store") {
|
||||||
|
self.isWireGuardAppStorePageVisible = true
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
|
||||||
|
SwiftUI.Button("Continue") {
|
||||||
|
self.showNextStep()
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var wireguardConfigView: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Enable the WireGuard Tunnel")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .network)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("Once WireGuard is installed, a configuration file has to be installed in the WireGuard app.")
|
||||||
|
Text("Tap the button below and open the downloaded file in the WireGuard app.")
|
||||||
|
Text("Then, activate the VPN tunnel to continue.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
VStack {
|
||||||
|
SwiftUI.Button("Download and Install Configuration File") {
|
||||||
|
self.downloadWireGuardProfile()
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle(isLoading: self.isDownloadingWireGuardProfile))
|
||||||
|
.sheet(item: self.$wireGuardProfileFileURL) { fileURL in
|
||||||
|
ActivityView(items: [fileURL])
|
||||||
|
}
|
||||||
|
|
||||||
|
SwiftUI.Button(self.isWireGuardTunnelReachable ? "Continue" : "Waiting for connection...",
|
||||||
|
action: self.showNextStep)
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
.disabled(!self.isWireGuardTunnelReachable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var addSourcesView: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Add Sources")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .booksVertical)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("All apps are provided through sources, which anyone can create and share with the world.")
|
||||||
|
Text("We have compiled a list of trusted sources for SideStore which you can enable to start sideloading your favorite apps.")
|
||||||
|
Text("By default, only the source containing SideStore itself is enabled.")
|
||||||
|
|
||||||
|
Toggle("Enable Trusted Sources", isOn: $areTrustedSourcesEnabled)
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
SwiftUI.Button("Continue") {
|
||||||
|
self.setupTrustedSources()
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle(isLoading: self.isLoadingTrustedSources))
|
||||||
|
.disabled(self.isLoadingTrustedSources)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var finishView: some View {
|
||||||
|
OnboardingStepView(title: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Setup Completed")
|
||||||
|
}
|
||||||
|
}, hero: {
|
||||||
|
Image(systemSymbol: .checkmark)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.shadow(color: .accentColor.opacity(0.8), radius: 12)
|
||||||
|
}, content: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("Congratulations, you did it! 🎉")
|
||||||
|
Text("You can start your sideloading journey.")
|
||||||
|
}
|
||||||
|
}, action: {
|
||||||
|
SwiftUI.Button("Let's Go") {
|
||||||
|
self.finishOnboarding()
|
||||||
|
}
|
||||||
|
.buttonStyle(FilledButtonStyle())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OnboardingView {
|
||||||
|
func importPairingFile(url: URL) {
|
||||||
|
let isSecuredURL = url.startAccessingSecurityScopedResource() == true
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Read to a string
|
||||||
|
let data = try Data(contentsOf: url)
|
||||||
|
let pairing_string = String(bytes: data, encoding: .utf8)
|
||||||
|
if pairing_string == nil {
|
||||||
|
// TODO: Show error message
|
||||||
|
debugPrint("Unable to read pairing file")
|
||||||
|
// displayError("Unable to read pairing file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to a file for next launch
|
||||||
|
let filename = "ALTPairingFile.mobiledevicepairing"
|
||||||
|
let fm = FileManager.default
|
||||||
|
let documentsPath = fm.documentsDirectory.appendingPathComponent("/\(filename)")
|
||||||
|
try pairing_string?.write(to: documentsPath, atomically: true, encoding: String.Encoding.utf8)
|
||||||
|
|
||||||
|
// Start minimuxer now that we have a file
|
||||||
|
start_minimuxer_threads(pairing_string!)
|
||||||
|
|
||||||
|
// Show the next onboarding step
|
||||||
|
self.showNextStep()
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSecuredURL) {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func start_minimuxer_threads(_ pairing_file: String) {
|
||||||
|
set_usbmuxd_socket()
|
||||||
|
#if false // Retries
|
||||||
|
var res = start_minimuxer(pairing_file: pairing_file)
|
||||||
|
var attempts = 10
|
||||||
|
while (attempts != 0 && res != 0) {
|
||||||
|
print("start_minimuxer `res` != 0, retry #\(attempts)")
|
||||||
|
res = start_minimuxer(pairing_file: pairing_file)
|
||||||
|
attempts -= 1
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
let res = start_minimuxer(pairing_file: pairing_file)
|
||||||
|
#endif
|
||||||
|
if res != 0 {
|
||||||
|
// TODO: Show error message
|
||||||
|
debugPrint("minimuxer failed to start. Incorrect arguments were passed.")
|
||||||
|
// displayError("minimuxer failed to start. Incorrect arguments were passed.")
|
||||||
|
}
|
||||||
|
auto_mount_dev_image()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OnboardingView {
|
||||||
|
func downloadWireGuardProfile() {
|
||||||
|
let profileDownloadUrl = "https://github.com/SideStore/SideStore/releases/download/0.3.1/SideStore.conf"
|
||||||
|
let destinationUrl = FileManager.default.temporaryDirectory.appendingPathComponent("SideStore.conf")
|
||||||
|
|
||||||
|
self.isDownloadingWireGuardProfile = true
|
||||||
|
URLSession.shared.dataTask(with: URLRequest(url: URL(string: profileDownloadUrl)!)) { data, response, error in
|
||||||
|
|
||||||
|
defer { self.isDownloadingWireGuardProfile = false }
|
||||||
|
|
||||||
|
if let error {
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let response = response as? HTTPURLResponse, 200..<300 ~= response.statusCode, let data else {
|
||||||
|
// TODO: Show error message
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try data.write(to: destinationUrl)
|
||||||
|
self.wireGuardProfileFileURL = destinationUrl
|
||||||
|
} catch {
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startPingingWireGuardTunnel() {
|
||||||
|
do {
|
||||||
|
self.reachabilityNotifier = try Reachability(hostname: "10.7.0.1")
|
||||||
|
self.reachabilityNotifier?.whenReachable = { _ in
|
||||||
|
self.isWireGuardTunnelReachable = true
|
||||||
|
}
|
||||||
|
self.reachabilityNotifier?.whenUnreachable = { _ in
|
||||||
|
self.isWireGuardTunnelReachable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.reachabilityNotifier?.startNotifier()
|
||||||
|
} catch {
|
||||||
|
// TODO: Show error message
|
||||||
|
debugPrint(error)
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopPingingWireGuardTunnel() {
|
||||||
|
self.reachabilityNotifier?.stopNotifier()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OnboardingView {
|
||||||
|
func setupTrustedSources() {
|
||||||
|
self.isLoadingTrustedSources = true
|
||||||
|
|
||||||
|
AppManager.shared.fetchTrustedSources { result in
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case .success(let trustedSources):
|
||||||
|
// Cache trusted source IDs.
|
||||||
|
UserDefaults.shared.trustedSourceIDs = trustedSources.map { $0.identifier }
|
||||||
|
|
||||||
|
// Don't show sources without a sourceURL.
|
||||||
|
let featuredSourceURLs = trustedSources.compactMap { $0.sourceURL }
|
||||||
|
|
||||||
|
// This context is never saved, but keeps the managed sources alive.
|
||||||
|
let context = DatabaseManager.shared.persistentContainer.newBackgroundSavingViewContext()
|
||||||
|
|
||||||
|
let dispatchGroup = DispatchGroup()
|
||||||
|
for sourceURL in featuredSourceURLs {
|
||||||
|
dispatchGroup.enter()
|
||||||
|
|
||||||
|
AppManager.shared.fetchSource(sourceURL: sourceURL, managedObjectContext: context) { result in
|
||||||
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.notify(queue: .main) {
|
||||||
|
self.isLoadingTrustedSources = false
|
||||||
|
|
||||||
|
// Save the fetched trusted sources
|
||||||
|
do {
|
||||||
|
try context.save()
|
||||||
|
} catch {
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.showNextStep()
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
NotificationManager.shared.reportError(error: error)
|
||||||
|
self.isLoadingTrustedSources = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OnboardingView {
|
||||||
|
func finishOnboarding() {
|
||||||
|
// Set the onboarding complete flag
|
||||||
|
UserDefaults.standard.onboardingComplete = true
|
||||||
|
|
||||||
|
if let onDismiss {
|
||||||
|
onDismiss()
|
||||||
|
} else {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct OnboardingView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Color.red
|
||||||
|
.ignoresSafeArea()
|
||||||
|
.sheet(isPresented: .constant(true)) {
|
||||||
|
OnboardingView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ public extension UserDefaults
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
@NSManaged var firstLaunch: Date?
|
@NSManaged var firstLaunch: Date?
|
||||||
|
@NSManaged var onboardingComplete: Bool
|
||||||
@NSManaged var requiresAppGroupMigration: Bool
|
@NSManaged var requiresAppGroupMigration: Bool
|
||||||
@NSManaged var textServer: Bool
|
@NSManaged var textServer: Bool
|
||||||
@NSManaged var textInputAnisetteURL: String?
|
@NSManaged var textInputAnisetteURL: String?
|
||||||
@@ -71,6 +72,7 @@ public extension UserDefaults
|
|||||||
let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14)
|
let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14)
|
||||||
|
|
||||||
let defaults = [
|
let defaults = [
|
||||||
|
#keyPath(UserDefaults.onboardingComplete): false,
|
||||||
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
|
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
|
||||||
#keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported,
|
#keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported,
|
||||||
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,
|
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,
|
||||||
|
|||||||
Reference in New Issue
Block a user