move Peered to implementation directory
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
BUILT_PRODUCTS_DIR="/Users/oschly/Library/Developer/Xcode/DerivedData/Peered-bkluzpjqedjbwtdpxqlesbwlyrjo/Build/Products/Debug-iphonesimulator"
|
||||
EXECUTABLE_PATH="Peered.app/Peered"
|
||||
FULL_PRODUCT_NAME="Peered.app"
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"label": "Build",
|
||||
"command": "xcede build",
|
||||
"allow_concurrent_runs": false,
|
||||
"reveal": "no_focus",
|
||||
"hide": "never"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
device="iPhone 16"
|
||||
platform=sim
|
||||
scheme="Peered"
|
||||
@@ -0,0 +1,391 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
479E81A72DD09F9400B82386 /* Peered.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Peered.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
47A799962E47D5400072440E /* Exceptions for "Peered" folder in "Peered" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 479E81A62DD09F9400B82386 /* Peered */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
479E81A92DD09F9400B82386 /* Peered */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
47A799962E47D5400072440E /* Exceptions for "Peered" folder in "Peered" target */,
|
||||
);
|
||||
path = Peered;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
479E81A42DD09F9400B82386 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
479E819E2DD09F9400B82386 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
479E81A92DD09F9400B82386 /* Peered */,
|
||||
479E81A82DD09F9400B82386 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
479E81A82DD09F9400B82386 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
479E81A72DD09F9400B82386 /* Peered.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
479E81A62DD09F9400B82386 /* Peered */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 479E81C92DD09F9500B82386 /* Build configuration list for PBXNativeTarget "Peered" */;
|
||||
buildPhases = (
|
||||
479E81A32DD09F9400B82386 /* Sources */,
|
||||
479E81A42DD09F9400B82386 /* Frameworks */,
|
||||
479E81A52DD09F9400B82386 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
479E81A92DD09F9400B82386 /* Peered */,
|
||||
);
|
||||
name = Peered;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = Peered;
|
||||
productReference = 479E81A72DD09F9400B82386 /* Peered.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
479E819F2DD09F9400B82386 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1630;
|
||||
LastUpgradeCheck = 1640;
|
||||
TargetAttributes = {
|
||||
479E81A62DD09F9400B82386 = {
|
||||
CreatedOnToolsVersion = 16.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 479E81A22DD09F9400B82386 /* Build configuration list for PBXProject "Peered" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 479E819E2DD09F9400B82386;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 479E81A82DD09F9400B82386 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
479E81A62DD09F9400B82386 /* Peered */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
479E81A52DD09F9400B82386 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
479E81A32DD09F9400B82386 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
479E81C72DD09F9500B82386 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = LTFJ368N25;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
479E81C82DD09F9500B82386 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = LTFJ368N25;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
479E81CA2DD09F9500B82386 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Peered/Peered.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = LTFJ368N25;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Peered/Info.plist;
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "";
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.oschly.Peered;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = auto;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
XROS_DEPLOYMENT_TARGET = 2.4;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
479E81CB2DD09F9500B82386 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Peered/Peered.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = LTFJ368N25;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Peered/Info.plist;
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "";
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
||||
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.oschly.Peered;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SDKROOT = auto;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
XROS_DEPLOYMENT_TARGET = 2.4;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
479E81A22DD09F9400B82386 /* Build configuration list for PBXProject "Peered" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
479E81C72DD09F9500B82386 /* Debug */,
|
||||
479E81C82DD09F9500B82386 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
479E81C92DD09F9500B82386 /* Build configuration list for PBXNativeTarget "Peered" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
479E81CA2DD09F9500B82386 /* Debug */,
|
||||
479E81CB2DD09F9500B82386 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 479E819F2DD09F9400B82386 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// CommunicationProvider.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 11/05/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MultipeerConnectivity
|
||||
|
||||
@Observable
|
||||
final class CommunicationProvider: NSObject, MCNearbyServiceBrowserDelegate {
|
||||
private let session: MCSession
|
||||
private let browser: MCNearbyServiceBrowser
|
||||
private let advertiser: MCNearbyServiceAdvertiser
|
||||
|
||||
private(set) var availablePeers: [MCPeerID] = []
|
||||
|
||||
init(id: String) {
|
||||
let id = MCPeerID(displayName: id)
|
||||
session = MCSession(peer: id, securityIdentity: nil, encryptionPreference: .required)
|
||||
browser = MCNearbyServiceBrowser(peer: id, serviceType: "peered")
|
||||
advertiser = MCNearbyServiceAdvertiser(
|
||||
peer: browser.myPeerID,
|
||||
discoveryInfo: [:],
|
||||
serviceType: "peered"
|
||||
)
|
||||
super.init()
|
||||
browser.delegate = self
|
||||
start()
|
||||
}
|
||||
|
||||
func start() {
|
||||
advertiser.startAdvertisingPeer()
|
||||
browser.startBrowsingForPeers()
|
||||
}
|
||||
|
||||
func stop() {
|
||||
browser.stopBrowsingForPeers()
|
||||
advertiser.stopAdvertisingPeer()
|
||||
}
|
||||
|
||||
func browser(
|
||||
_ browser: MCNearbyServiceBrowser,
|
||||
foundPeer peerID: MCPeerID,
|
||||
withDiscoveryInfo info: [String: String]?
|
||||
) {
|
||||
availablePeers.append(peerID)
|
||||
browser.invitePeer(peerID, to: session, withContext: nil, timeout: 30)
|
||||
}
|
||||
|
||||
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
|
||||
availablePeers.removeAll { id in
|
||||
id == peerID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 11/05/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension EnvironmentValues {
|
||||
@Entry var ownPeer: OwnPeer = .fallback
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@AppStorage("peered_username") private var username: String = "fallback_user"
|
||||
@State private var notes = [Note]()
|
||||
@State private var notesClient: NoteEditingSessionClient?
|
||||
@State private var ownPeer: OwnPeer?
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
Section("Your notes") {
|
||||
ForEach(notes) { note in
|
||||
NavigationLink(note.name) {
|
||||
let peer = ownPeer ?? .init(peer: .init(displayName: username))
|
||||
if ownPeer == nil {
|
||||
ownPeer = peer
|
||||
}
|
||||
return NoteEditorScreen(note: note, peer: peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let notesClient {
|
||||
Section("External notes") {
|
||||
ForEach(notesClient.invitations) { invitation in
|
||||
NavigationLink(invitation.noteName) {
|
||||
SharedNoteEditor(invitation: invitation, noteClient: notesClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.environment(\.ownPeer, ownPeer ?? .fallback)
|
||||
.navigationTitle("Peered")
|
||||
.toolbar {
|
||||
Button("Create note") {
|
||||
NotesStorage().createNote(name: "New Note")
|
||||
notes = NotesStorage().loadNotes()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
notes = NotesStorage().loadNotes()
|
||||
if notesClient == nil {
|
||||
notesClient = .init(peer: .init(displayName: username))
|
||||
}
|
||||
notesClient?.startBrowsingForNotes()
|
||||
}
|
||||
.sheet(isPresented: .constant(username == "fallback_user" || username.isEmpty)) {
|
||||
SetUserNameBottomSheetView(username: $username)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_peered._tcp</string>
|
||||
</array>
|
||||
<key>NSServices</key>
|
||||
<array>
|
||||
<dict/>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ManageMembersView.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 06/10/2025.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct ManageMembersView: View {
|
||||
@Bindable var noteAdvertiser: NoteEditingSessionServer
|
||||
let noteTitle: String
|
||||
@Binding var noteContent: String
|
||||
|
||||
var body: some View {
|
||||
List(noteAdvertiser.visiblePeers) { peer in
|
||||
HStack {
|
||||
Text(peer.id)
|
||||
|
||||
Spacer()
|
||||
|
||||
PeerStateButton(peerState: peer.state) {
|
||||
noteAdvertiser.invite(peer: peer, to: .init(title: noteTitle, noteSnapshot: noteContent))
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Visible users")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import MultipeerConnectivity
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
struct OwnPeer {
|
||||
let peer: MCPeerID
|
||||
|
||||
static var fallback: Self { Self(peer: .init(displayName: "fallback_user")) }
|
||||
}
|
||||
|
||||
struct Peer: Identifiable {
|
||||
enum ConnectionState {
|
||||
case available
|
||||
case joined
|
||||
case rejected
|
||||
case invitationPending
|
||||
}
|
||||
|
||||
var id: String {
|
||||
mcPeer.displayName
|
||||
}
|
||||
let mcPeer: MCPeerID
|
||||
var state: ConnectionState
|
||||
}
|
||||
|
||||
@Observable
|
||||
final class NoteEditingSessionServer: NSObject {
|
||||
private let session: MCSession
|
||||
private let browser: MCNearbyServiceBrowser
|
||||
private let ownPeer: OwnPeer
|
||||
|
||||
var visiblePeers: [Peer] = []
|
||||
let noteChangesEmitter = PassthroughSubject<String, Never>()
|
||||
|
||||
init(peer: OwnPeer) {
|
||||
ownPeer = peer
|
||||
browser = .init(peer: peer.peer, serviceType: "peered")
|
||||
session = .init(peer: peer.peer)
|
||||
super.init()
|
||||
browser.delegate = self
|
||||
session.delegate = self
|
||||
}
|
||||
|
||||
func startServer() {
|
||||
browser.startBrowsingForPeers()
|
||||
}
|
||||
|
||||
func stopServer() {
|
||||
browser.stopBrowsingForPeers()
|
||||
}
|
||||
|
||||
func invite(peer: Peer, to note: NoteInvitation.NoteContent) {
|
||||
guard peer.state == .available else { return }
|
||||
browser.invitePeer(
|
||||
peer.mcPeer,
|
||||
to: session,
|
||||
withContext: try! JSONEncoder().encode(note),
|
||||
timeout: 5
|
||||
)
|
||||
|
||||
let idxToUpdate = visiblePeers.firstIndex(where: { $0.mcPeer == peer.mcPeer })!
|
||||
visiblePeers[idxToUpdate].state = .invitationPending
|
||||
}
|
||||
}
|
||||
|
||||
extension NoteEditingSessionServer: MCNearbyServiceBrowserDelegate {
|
||||
func browser(
|
||||
_ browser: MCNearbyServiceBrowser,
|
||||
foundPeer peerID: MCPeerID,
|
||||
withDiscoveryInfo info: [String : String]?
|
||||
) {
|
||||
guard !visiblePeers.contains(where: { $0.mcPeer == peerID }) && peerID.displayName != ownPeer.peer.displayName else { return }
|
||||
let newPeer = Peer(mcPeer: peerID, state: .available)
|
||||
visiblePeers.append(newPeer)
|
||||
}
|
||||
|
||||
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
|
||||
guard let peerIdx = visiblePeers.firstIndex(where: { $0.mcPeer == peerID }) else { return }
|
||||
visiblePeers.remove(at: peerIdx)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoteEditingSessionServer: MCSessionDelegate {
|
||||
func session(
|
||||
_ session: MCSession,
|
||||
peer peerID: MCPeerID,
|
||||
didChange state: MCSessionState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
func session(
|
||||
_ session: MCSession,
|
||||
didReceive stream: InputStream,
|
||||
withName streamName: String,
|
||||
fromPeer peerID: MCPeerID
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
func session(
|
||||
_ session: MCSession,
|
||||
didStartReceivingResourceWithName resourceName: String,
|
||||
fromPeer peerID: MCPeerID,
|
||||
with progress: Progress
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
func session(
|
||||
_ session: MCSession,
|
||||
didFinishReceivingResourceWithName resourceName: String,
|
||||
fromPeer peerID: MCPeerID,
|
||||
at localURL: URL?,
|
||||
withError error: (any Error)?
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
|
||||
guard let note = String(data: data, encoding: .utf8) else { fatalError() }
|
||||
noteChangesEmitter.send(note)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// NoteEditorScreen.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 25/09/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MultipeerConnectivity
|
||||
|
||||
struct NoteEditorScreen: View {
|
||||
let note: Note
|
||||
@State private var noteAdvertiser: NoteEditingSessionServer
|
||||
@State private var noteContent: String = ""
|
||||
@State private var timer = Timer.publish(every: 5, on: .current, in: .common)
|
||||
.autoconnect()
|
||||
@State private var showManageMembers = false
|
||||
|
||||
init(note: Note, peer: OwnPeer) {
|
||||
self.note = note
|
||||
self._noteAdvertiser = .init(initialValue: .init(peer: peer))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
TextEditor(text: $noteContent)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("Manage members") {
|
||||
showManageMembers = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showManageMembers) {
|
||||
NavigationStack {
|
||||
ManageMembersView(
|
||||
noteAdvertiser: noteAdvertiser,
|
||||
noteTitle: note.name,
|
||||
noteContent: $noteContent
|
||||
)
|
||||
}
|
||||
}
|
||||
.onReceive(noteAdvertiser.noteChangesEmitter) { updatedNote in
|
||||
self.noteContent = updatedNote
|
||||
}
|
||||
.onAppear {
|
||||
noteContent = try! String(contentsOf: note.path, encoding: .utf8)
|
||||
noteAdvertiser.startServer()
|
||||
}
|
||||
.onDisappear {
|
||||
noteAdvertiser.stopServer()
|
||||
saveNote()
|
||||
}
|
||||
.onReceive(timer) { _ in
|
||||
saveNote()
|
||||
}
|
||||
}
|
||||
|
||||
func saveNote() {
|
||||
try! noteContent.write(to: note.path, atomically: true, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// PeerStateButton.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 06/10/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PeerStateButton: View {
|
||||
let peerState: Peer.ConnectionState
|
||||
let onTap: () -> Void
|
||||
|
||||
var body: some View {
|
||||
switch peerState {
|
||||
case .available:
|
||||
Button("Invite", action: onTap)
|
||||
case .joined:
|
||||
Text("Joined")
|
||||
case .rejected:
|
||||
Text("Rejected")
|
||||
case .invitationPending:
|
||||
Text("Invitation pending")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// SharedNoteEditor.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 07/10/2025.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct SharedNoteEditor: View {
|
||||
@State var note: String?
|
||||
@State var sendTask: Task<Void, Never>?
|
||||
@State var invitation: NoteInvitation
|
||||
@Bindable var noteClient: NoteEditingSessionClient
|
||||
|
||||
init(
|
||||
invitation: NoteInvitation,
|
||||
noteClient: NoteEditingSessionClient
|
||||
) {
|
||||
self._invitation = .init(initialValue: invitation)
|
||||
self._noteClient = .init(noteClient)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let note = Binding($note) {
|
||||
TextEditor(text: note)
|
||||
} else {
|
||||
ProgressView {
|
||||
Text("Fetching note...")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: note) { _, newValue in
|
||||
sendTask?.cancel()
|
||||
sendTask = Task {
|
||||
try? await Task.sleep(nanoseconds: 500000000)
|
||||
guard let note else { return }
|
||||
DispatchQueue.main.async {
|
||||
noteClient.send(note: note, to: invitation.invitatorID)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
invitation.accept()
|
||||
note = invitation.note.noteSnapshot
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Note.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 25/09/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Note: Identifiable {
|
||||
var id: URL { path }
|
||||
|
||||
let name: String
|
||||
let path: URL
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import MultipeerConnectivity
|
||||
import Foundation
|
||||
|
||||
struct NoteInvitation: Identifiable {
|
||||
struct NoteContent: Codable {
|
||||
let title: String
|
||||
let noteSnapshot: String
|
||||
}
|
||||
|
||||
var id: MCPeerID { invitatorID }
|
||||
let noteName: String
|
||||
let invitatorID: MCPeerID
|
||||
let note: NoteContent
|
||||
private var invitationHandler: ((Bool) -> Void)?
|
||||
|
||||
init(
|
||||
noteName: String,
|
||||
invitatorID: MCPeerID,
|
||||
note: NoteContent,
|
||||
invitationHandler: ((Bool) -> Void)? = nil
|
||||
) {
|
||||
self.noteName = noteName
|
||||
self.invitatorID = invitatorID
|
||||
self.note = note
|
||||
self.invitationHandler = invitationHandler
|
||||
}
|
||||
|
||||
mutating func accept() {
|
||||
invitationHandler?(true)
|
||||
invitationHandler = nil
|
||||
}
|
||||
|
||||
mutating func decline() {
|
||||
invitationHandler?(false)
|
||||
invitationHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
@Observable
|
||||
final class NoteEditingSessionClient: NSObject {
|
||||
private let session: MCSession
|
||||
private let advertiser: MCNearbyServiceAdvertiser
|
||||
private let ownPeer: MCPeerID
|
||||
|
||||
var invitations: [NoteInvitation] = [] {
|
||||
didSet {
|
||||
print(invitations)
|
||||
}
|
||||
}
|
||||
|
||||
init(peer: MCPeerID) {
|
||||
ownPeer = peer
|
||||
session = MCSession(
|
||||
peer: peer,
|
||||
securityIdentity: nil,
|
||||
encryptionPreference: .required
|
||||
)
|
||||
advertiser = MCNearbyServiceAdvertiser(
|
||||
peer: peer,
|
||||
discoveryInfo: [:],
|
||||
serviceType: "peered"
|
||||
)
|
||||
super.init()
|
||||
advertiser.delegate = self
|
||||
}
|
||||
|
||||
func startBrowsingForNotes() {
|
||||
advertiser.startAdvertisingPeer()
|
||||
}
|
||||
|
||||
func stopBrowsingForNotes() {
|
||||
advertiser.stopAdvertisingPeer()
|
||||
}
|
||||
|
||||
func send(note: String, to peer: MCPeerID) {
|
||||
try! session.send(
|
||||
note.data(using: .utf8)!,
|
||||
toPeers: [peer],
|
||||
with: .reliable
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoteEditingSessionClient: MCNearbyServiceAdvertiserDelegate {
|
||||
func advertiser(
|
||||
_ advertiser: MCNearbyServiceAdvertiser,
|
||||
didReceiveInvitationFromPeer peerID: MCPeerID,
|
||||
withContext context: Data?,
|
||||
invitationHandler: @escaping (Bool, MCSession?) -> Void
|
||||
) {
|
||||
guard
|
||||
let context,
|
||||
let noteContent = try? JSONDecoder().decode(NoteInvitation.NoteContent.self, from: context)
|
||||
else { fatalError() }
|
||||
|
||||
invitations.append(
|
||||
.init(
|
||||
noteName: noteContent.title,
|
||||
invitatorID: peerID,
|
||||
note: noteContent,
|
||||
invitationHandler: { [weak self, invitationHandler] accepted in
|
||||
guard let self else { return }
|
||||
invitationHandler(accepted, self.session)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
guard let idx = self.invitations.firstIndex(where: { $0.id == peerID }) else { return }
|
||||
self.invitations.remove(at: idx)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import SwiftUI
|
||||
|
||||
struct NoteInvitationView: View {
|
||||
@Binding var invitation: NoteInvitation
|
||||
let joinTapped: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(invitation.noteName)
|
||||
Spacer()
|
||||
Button("Join", action: joinTapped)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// NotesStorage.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 25/09/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NotesStorage {
|
||||
let storageProvider: FileManager = FileManager.default
|
||||
|
||||
func loadNotes() -> [Note] {
|
||||
let files = try! storageProvider
|
||||
.contentsOfDirectory(atPath: URL.documentsDirectory.path)
|
||||
var notes = [Note]()
|
||||
|
||||
for file in files.compactMap({
|
||||
URL(
|
||||
filePath: $0,
|
||||
directoryHint: .notDirectory,
|
||||
relativeTo: URL.documentsDirectory
|
||||
)
|
||||
}) {
|
||||
let name = file.lastPathComponent
|
||||
let note = Note(
|
||||
name: name,
|
||||
path: file
|
||||
)
|
||||
|
||||
notes.append(note)
|
||||
}
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
func createNote(name: String) {
|
||||
let currentNotes = loadNotes()
|
||||
var proposedName = name
|
||||
var index: Int? = nil
|
||||
|
||||
while currentNotes.contains(where: { $0.name == proposedName }) {
|
||||
if let _index = index {
|
||||
index = _index + 1
|
||||
} else {
|
||||
index = 1
|
||||
}
|
||||
}
|
||||
|
||||
proposedName = if let index {
|
||||
"\(proposedName) \(index)"
|
||||
} else {
|
||||
proposedName
|
||||
}
|
||||
let pathToWrite = URL.documentsDirectory.appendingPathComponent(proposedName).appendingPathExtension(for: .text)
|
||||
try! String().write(to: pathToWrite, atomically: true, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// PeeredApp.swift
|
||||
// Peered
|
||||
//
|
||||
// Created by Oskar Chybowski on 11/05/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct PeeredApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SetUserNameBottomSheetView: View {
|
||||
@State private var proposedUsername: String = ""
|
||||
@Binding var username: String
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Set your username") {
|
||||
TextField("Username", text: $proposedUsername)
|
||||
}
|
||||
|
||||
Button("Save username") {
|
||||
username = proposedUsername
|
||||
}
|
||||
}
|
||||
.interactiveDismissDisabled()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "xcode build server",
|
||||
"version": "0.2",
|
||||
"bspVersion": "2.0",
|
||||
"languages": [
|
||||
"c",
|
||||
"cpp",
|
||||
"objective-c",
|
||||
"objective-cpp",
|
||||
"swift"
|
||||
],
|
||||
"argv": [
|
||||
"/opt/homebrew/bin/xcode-build-server"
|
||||
],
|
||||
"workspace": "/Users/oschly/Developer/Peered/Peered.xcodeproj/project.xcworkspace",
|
||||
"build_root": "/Users/oschly/Library/Developer/Xcode/DerivedData/Peered-bkluzpjqedjbwtdpxqlesbwlyrjo",
|
||||
"scheme": "Peered",
|
||||
"kind": "xcode"
|
||||
}
|
||||
Reference in New Issue
Block a user