mirror of
https://github.com/leonboe1/QuickDrop.git
synced 2026-11-11 19:11:10 +00:00
Add Local Network Checker
This commit is contained in:
1 parent
b61aecf315
commit
fb13405247
5 files changed
+6078
-6173
No files matched your search
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 70;
|
||||
objectVersion = 60;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -124,6 +124,7 @@
|
||||
EB7654A12E53252D006B4AA3 /* AttachmentDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB950B762E521712002D68FA /* AttachmentDetails.swift */; };
|
||||
EB7654C02E534A8E006B4AA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69DA9A1329E0BF5200A442DA /* Assets.xcassets */; };
|
||||
EB7654C12E534A8E006B4AA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69DA9A1329E0BF5200A442DA /* Assets.xcassets */; };
|
||||
EB7A95F42EF6E1BE0021111B /* IntroductionSplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7A95F22EF6E1BE0021111B /* IntroductionSplashView.swift */; };
|
||||
EB7F54122D78E96000989564 /* TutorialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7F54112D78E95C00989564 /* TutorialView.swift */; };
|
||||
EB7F54142D78E9E700989564 /* LicenseWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7F54132D78E9E400989564 /* LicenseWindow.swift */; };
|
||||
EB821C992E76FE5300036963 /* SmallSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB96ED8A2D7F7CF4004B3FF1 /* SmallSheetView.swift */; };
|
||||
@@ -355,6 +356,7 @@
|
||||
EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
EB721F232E75AFA900F2099F /* QuickDropIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = QuickDropIcon.icon; sourceTree = "<group>"; };
|
||||
EB7654772E525ACC006B4AA3 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||
EB7A95F22EF6E1BE0021111B /* IntroductionSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroductionSplashView.swift; sourceTree = "<group>"; };
|
||||
EB7F54112D78E95C00989564 /* TutorialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TutorialView.swift; sourceTree = "<group>"; };
|
||||
EB7F54132D78E9E400989564 /* LicenseWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicenseWindow.swift; sourceTree = "<group>"; };
|
||||
EB869DB42E92AA33009AAAD4 /* QuickDropToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickDropToastView.swift; sourceTree = "<group>"; };
|
||||
@@ -393,10 +395,6 @@
|
||||
EBE5FF4A2EDB8D4B0019B380 /* Config.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Config.storekit; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
EB7A95582EF67C860021111B /* Introduction */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Introduction; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
699DEB9E2AB0573200115D22 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
@@ -630,6 +628,14 @@
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EB7A95F32EF6E1BE0021111B /* Introduction */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EB7A95F22EF6E1BE0021111B /* IntroductionSplashView.swift */,
|
||||
);
|
||||
path = Introduction;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EB950B6C2E5206E2002D68FA /* iOSShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -661,7 +667,7 @@
|
||||
EB7F54112D78E95C00989564 /* TutorialView.swift */,
|
||||
EB7F54132D78E9E400989564 /* LicenseWindow.swift */,
|
||||
EB869DB42E92AA33009AAAD4 /* QuickDropToastView.swift */,
|
||||
EB7A95582EF67C860021111B /* Introduction */,
|
||||
EB7A95F32EF6E1BE0021111B /* Introduction */,
|
||||
EBA1BB1C2EF1F5650068E46E /* ChangeDeviceNameModifier.swift */,
|
||||
EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */,
|
||||
EB6F62D62D91F4AC00534EC2 /* SettingsView.swift */,
|
||||
@@ -741,9 +747,6 @@
|
||||
dependencies = (
|
||||
699DEBAD2AB0573200115D22 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
EB7A95582EF67C860021111B /* Introduction */,
|
||||
);
|
||||
name = QuickDrop;
|
||||
packageProductDependencies = (
|
||||
EB2F9BFD2DA05F48004010A2 /* BezelNotification */,
|
||||
@@ -1043,6 +1046,7 @@
|
||||
EB1325F62E7025D900FD749C /* ApIsolation6Tester.swift in Sources */,
|
||||
EBC05C842E33B58300EECC18 /* IssueView.swift in Sources */,
|
||||
EBC05C852E33B58300EECC18 /* NetworkFilterIssueView.swift in Sources */,
|
||||
EB7A95F42EF6E1BE0021111B /* IntroductionSplashView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1332,7 +1336,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SHARED_BUILD = 99;
|
||||
SHARED_BUILD = 101;
|
||||
SHARED_VERSION = 2.10;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
@@ -1395,7 +1399,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SHARED_BUILD = 99;
|
||||
SHARED_BUILD = 101;
|
||||
SHARED_VERSION = 2.10;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
|
||||
@@ -5,233 +5,11 @@
|
||||
"comment" : "Bundle display name",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "QuickDrop"
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -239,233 +17,11 @@
|
||||
"comment" : "Bundle name",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "QuickDrop"
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -473,239 +29,16 @@
|
||||
"comment" : "Copyright (human-readable)",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"cs" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"da" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"el" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-AU" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"en-GB" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"es-419" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"hu" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"id" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ms" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nb" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"nl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"pt-PT" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ro" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"sv" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"th" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"uk" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"vi" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NSLocalNetworkUsageDescription" : {
|
||||
"comment" : "Privacy - Local Network Usage Description",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"ar" : {
|
||||
"stringUnit" : {
|
||||
|
||||
+5932
-5466
File diff suppressed because it is too large.
Load diff
@@ -13,30 +13,86 @@ final class DeviceToDeviceHeuristicScanner {
|
||||
static let shared = DeviceToDeviceHeuristicScanner()
|
||||
private init() {}
|
||||
|
||||
private let scanQueue = DispatchQueue(label: "DeviceToDeviceScanQueue")
|
||||
private var reachableIPs: [String] = []
|
||||
private var completion: ((Bool) -> Void)?
|
||||
private var finished = false
|
||||
|
||||
var success: Bool {
|
||||
self.reachableIPs.count > 0
|
||||
private enum ScanMode {
|
||||
case peerReachability // true only if some peer is reachable
|
||||
case localNetworkAccess // true if EHOSTDOWN seen (or ready)
|
||||
}
|
||||
|
||||
private let scanQueue = DispatchQueue(label: "DeviceToDeviceScanQueue")
|
||||
|
||||
private var reachableIPs: [String] = []
|
||||
private var activeConnections: [NWConnection] = []
|
||||
|
||||
private var completion: ((Bool) -> Void)?
|
||||
private var finished = false
|
||||
private var currentMode: ScanMode = .peerReachability
|
||||
|
||||
// Cache: once true, stays true for subsequent calls
|
||||
private var cachedLocalNetworkAccess = false
|
||||
|
||||
private var peerReachableCached: Bool { !reachableIPs.isEmpty }
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/// Start scanning the subnet for reachable peers.
|
||||
/// - Parameters:
|
||||
/// - baseSubnet: e.g. "192.168.1" — if nil, will auto-detect
|
||||
/// - completion: true if any peer device was reachable
|
||||
/// - Returns: true if any peer device was reachable (same semantics as before).
|
||||
func scan(subnet baseSubnet: String? = nil,
|
||||
port: UInt16 = 80,
|
||||
timeout: TimeInterval = 10.0,
|
||||
completion: @escaping (Bool) -> Void) {
|
||||
|
||||
if self.success {
|
||||
if peerReachableCached {
|
||||
completion(true)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
startScan(
|
||||
mode: .peerReachability,
|
||||
subnet: baseSubnet,
|
||||
port: port,
|
||||
timeout: timeout,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if:
|
||||
/// - cached value is true, OR
|
||||
/// - during a scan at least one host produces `.waiting(.posix(.EHOSTDOWN))`.
|
||||
///
|
||||
/// As soon as EHOSTDOWN is observed, returns true immediately.
|
||||
/// Otherwise waits until timeout (default 10s) and returns false.
|
||||
func hasLocalNetworkAccess(subnet baseSubnet: String? = nil,
|
||||
port: UInt16 = 80,
|
||||
timeout: TimeInterval = 10.0,
|
||||
completion: @escaping (Bool) -> Void) -> Bool {
|
||||
|
||||
if cachedLocalNetworkAccess {
|
||||
return true
|
||||
}
|
||||
|
||||
startScan(
|
||||
mode: .localNetworkAccess,
|
||||
subnet: baseSubnet,
|
||||
port: port,
|
||||
timeout: timeout,
|
||||
completion: completion
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Core scan logic
|
||||
|
||||
private func startScan(mode: ScanMode,
|
||||
subnet baseSubnet: String?,
|
||||
port: UInt16,
|
||||
timeout: TimeInterval,
|
||||
completion: @escaping (Bool) -> Void) {
|
||||
|
||||
scanQueue.async {
|
||||
// Cancel any in-flight scan to keep behavior deterministic.
|
||||
self.cancelActiveConnections()
|
||||
|
||||
self.currentMode = mode
|
||||
self.completion = completion
|
||||
self.finished = false
|
||||
|
||||
@@ -47,16 +103,17 @@ final class DeviceToDeviceHeuristicScanner {
|
||||
self.testConnection(to: ip, port: port)
|
||||
}
|
||||
|
||||
// Timeout fallback
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
|
||||
self.finish()
|
||||
self.finish(result: self.resultForCurrentModeOnTimeout())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func testConnection(to ip: String, port: UInt16) {
|
||||
guard let nwPort = NWEndpoint.Port(rawValue: port) else { return }
|
||||
|
||||
let connection = NWConnection(host: NWEndpoint.Host(ip), port: nwPort, using: .tcp)
|
||||
activeConnections.append(connection)
|
||||
|
||||
connection.stateUpdateHandler = { [weak self] state in
|
||||
guard let self else { return }
|
||||
@@ -65,12 +122,31 @@ final class DeviceToDeviceHeuristicScanner {
|
||||
case .ready:
|
||||
self.scanQueue.async {
|
||||
self.reachableIPs.append(ip)
|
||||
// If a TCP connection succeeds, local network access is effectively confirmed as well.
|
||||
self.cachedLocalNetworkAccess = true
|
||||
|
||||
// For scan(): success means peer reachable.
|
||||
// For hasLocalNetworkAccess(): success also means local network access.
|
||||
self.finish(result: true)
|
||||
}
|
||||
self.finish()
|
||||
connection.cancel()
|
||||
|
||||
case .waiting(let error):
|
||||
// "Host is down" is treated as a strong signal that local-network traffic is possible,
|
||||
// even if the specific host doesn't respond.
|
||||
if error == .posix(.EHOSTDOWN) {
|
||||
self.scanQueue.async {
|
||||
self.cachedLocalNetworkAccess = true
|
||||
|
||||
if self.currentMode == .localNetworkAccess {
|
||||
self.finish(result: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case .failed, .cancelled:
|
||||
connection.cancel()
|
||||
// Ignore; scan completes via early success or timeout.
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
@@ -80,20 +156,38 @@ final class DeviceToDeviceHeuristicScanner {
|
||||
connection.start(queue: scanQueue)
|
||||
}
|
||||
|
||||
private func finish() {
|
||||
private func resultForCurrentModeOnTimeout() -> Bool {
|
||||
switch currentMode {
|
||||
case .peerReachability:
|
||||
return peerReachableCached
|
||||
case .localNetworkAccess:
|
||||
return cachedLocalNetworkAccess
|
||||
}
|
||||
}
|
||||
|
||||
private func finish(result: Bool) {
|
||||
scanQueue.async {
|
||||
guard !self.finished else { return }
|
||||
self.finished = true
|
||||
|
||||
|
||||
let completion = self.completion
|
||||
self.completion = nil
|
||||
|
||||
self.cancelActiveConnections()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion?(self.success)
|
||||
completion?(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelActiveConnections() {
|
||||
for c in activeConnections { c.cancel() }
|
||||
activeConnections.removeAll()
|
||||
}
|
||||
|
||||
// MARK: - Subnet detection
|
||||
|
||||
/// Attempts to detect the local subnet prefix (e.g., "192.168.0")
|
||||
private static func getLocalSubnetPrefix() -> String? {
|
||||
var ifaddr: UnsafeMutablePointer<ifaddrs>?
|
||||
|
||||
@@ -67,7 +67,9 @@ struct IntroductionSplashView: View {
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
if let skipAction = currentPage.skipAction {
|
||||
if let skipAction = currentPage.skipAction,
|
||||
(currentPage.needRequestBeforeSkipAvailable == false || pollingPage != nil) {
|
||||
|
||||
Button("introduction_skip") {
|
||||
self.skipAction = skipAction
|
||||
}
|
||||
@@ -75,7 +77,7 @@ struct IntroductionSplashView: View {
|
||||
.buttonStyle(.borderless)
|
||||
.alert(item: $skipAction) { action in
|
||||
|
||||
.init(title: Text(action.warningTitle.localized()), message: Text(action.warningMessage.localized()), primaryButton: .default(Text("introduction_skip".localized()), action: {
|
||||
.init(title: Text(action.warningTitle.localized()), message: Text(action.warningMessage.localized()), primaryButton: .default(Text("introduction_continue".localized()), action: {
|
||||
|
||||
stopPolling()
|
||||
self.skipAction = nil
|
||||
@@ -181,11 +183,6 @@ struct IntroductionSplashView: View {
|
||||
|
||||
private func triggerLocalNetworkPermission() {
|
||||
self.startReceiving()
|
||||
|
||||
// just to be sure it is triggered
|
||||
runAfter(seconds: 1) {
|
||||
DeviceToDeviceHeuristicScanner.shared.scan(completion: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
private func advance() {
|
||||
@@ -251,16 +248,25 @@ enum IntroductionPage: CaseIterable {
|
||||
case .splash:
|
||||
return nil
|
||||
case .noWifi:
|
||||
return .init(action: {presentNextPage()}, warningTitle: "introduction_no_wifi_skip_title", warningMessage: "introduction_no_wifi_skip_message", id: "noWifiSkip")
|
||||
return .init(action: presentNextPage, warningTitle: "introduction_no_wifi_skip_title", warningMessage: "introduction_no_wifi_skip_message", id: "noWifiSkip")
|
||||
case .localNetworkAccess:
|
||||
return nil
|
||||
return .init(action: presentNextPage, warningTitle: "introduction_local_network_access_skip_title", warningMessage: "introduction_local_network_access_skip_message", id: "localNetworkAccessSkip")
|
||||
case .enableShareExtension:
|
||||
return .init(action: {presentNextPage()}, warningTitle: "introduction_enable_share_extension_skip_title", warningMessage: "introduction_enable_share_extension_skip_message", id: "enableShareExtensionSkip")
|
||||
return .init(action: presentNextPage, warningTitle: "introduction_enable_share_extension_skip_title", warningMessage: "introduction_enable_share_extension_skip_message", id: "enableShareExtensionSkip")
|
||||
case .finished:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var needRequestBeforeSkipAvailable: Bool {
|
||||
switch self {
|
||||
case .enableShareExtension:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var canPerformAction: Bool {
|
||||
switch self {
|
||||
case .noWifi:
|
||||
@@ -291,6 +297,8 @@ enum IntroductionPage: CaseIterable {
|
||||
return NearbyConnectionManager.shared.isConnectedToLocalNetwork
|
||||
case .enableShareExtension:
|
||||
return NSSharingService(named: NSSharingService.Name("com.leonboettger.neardrop.ShareExtension")) != nil
|
||||
case .localNetworkAccess:
|
||||
return DeviceToDeviceHeuristicScanner.shared.hasLocalNetworkAccess(completion: {_ in })
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user