Initial Commit

This commit is contained in:
aminecmi 2024-07-11 12:38:11 +02:00
parent dc63e0c91e
commit 7aa8fab7f5
20 changed files with 1127 additions and 0 deletions

131
.gitignore vendored Normal file
View File

@ -0,0 +1,131 @@
# Created by https://www.toptal.com/developers/gitignore/api/osx,swift,xcode
# Edit at https://www.toptal.com/developers/gitignore?templates=osx,swift,xcode
### OSX ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# Pods/
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# Accio dependency management
Dependencies/
.accio/
# fastlane
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### Xcode ###
## Xcode 8 and earlier
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings
# End of https://www.toptal.com/developers/gitignore/api/osx,swift,xcode

View File

@ -0,0 +1,406 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
67CF41D22C3FED90002906E5 /* Screaming_ReminderApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41D12C3FED90002906E5 /* Screaming_ReminderApp.swift */; };
67CF41D42C3FED90002906E5 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41D32C3FED90002906E5 /* ContentView.swift */; };
67CF41D62C3FED90002906E5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41D52C3FED90002906E5 /* Notification.swift */; };
67CF41D82C3FED91002906E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67CF41D72C3FED91002906E5 /* Assets.xcassets */; };
67CF41DB2C3FED91002906E5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67CF41DA2C3FED91002906E5 /* Preview Assets.xcassets */; };
67CF41E52C4080DE002906E5 /* Reminder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41E42C4080DE002906E5 /* Reminder.swift */; };
67CF41E72C408E73002906E5 /* NotificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41E62C408E73002906E5 /* NotificationDelegate.swift */; };
67CF41EB2C4157DA002906E5 /* NotificationsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CF41EA2C4157DA002906E5 /* NotificationsUtils.swift */; };
67E5F0382C4BED68005DDD7A /* SheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5F0372C4BED68005DDD7A /* SheetView.swift */; };
67E5F03A2C504501005DDD7A /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5F0392C504501005DDD7A /* Extensions.swift */; };
67E5F0402C53D898005DDD7A /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 67E5F03F2C53D898005DDD7A /* .gitignore */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
67CF41CE2C3FED90002906E5 /* Screaming Reminder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Screaming Reminder.app"; sourceTree = BUILT_PRODUCTS_DIR; };
67CF41D12C3FED90002906E5 /* Screaming_ReminderApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screaming_ReminderApp.swift; sourceTree = "<group>"; };
67CF41D32C3FED90002906E5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
67CF41D52C3FED90002906E5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
67CF41D72C3FED91002906E5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
67CF41DA2C3FED91002906E5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
67CF41E42C4080DE002906E5 /* Reminder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reminder.swift; sourceTree = "<group>"; };
67CF41E62C408E73002906E5 /* NotificationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDelegate.swift; sourceTree = "<group>"; };
67CF41EA2C4157DA002906E5 /* NotificationsUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsUtils.swift; sourceTree = "<group>"; };
67CF41EC2C41C5DF002906E5 /* TODO.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TODO.md; sourceTree = "<group>"; };
67E5F0372C4BED68005DDD7A /* SheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetView.swift; sourceTree = "<group>"; };
67E5F0392C504501005DDD7A /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
67E5F03F2C53D898005DDD7A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
67CF41CB2C3FED90002906E5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
67CF41C52C3FED90002906E5 = {
isa = PBXGroup;
children = (
67E5F03F2C53D898005DDD7A /* .gitignore */,
67CF41D02C3FED90002906E5 /* Screaming Reminder */,
67CF41CF2C3FED90002906E5 /* Products */,
);
sourceTree = "<group>";
};
67CF41CF2C3FED90002906E5 /* Products */ = {
isa = PBXGroup;
children = (
67CF41CE2C3FED90002906E5 /* Screaming Reminder.app */,
);
name = Products;
sourceTree = "<group>";
};
67CF41D02C3FED90002906E5 /* Screaming Reminder */ = {
isa = PBXGroup;
children = (
67E5F03C2C53CDD7005DDD7A /* Views */,
67E5F03B2C53CDC3005DDD7A /* Models */,
67CF41E12C406895002906E5 /* Utils */,
67CF41D12C3FED90002906E5 /* Screaming_ReminderApp.swift */,
67CF41D72C3FED91002906E5 /* Assets.xcassets */,
67CF41D92C3FED91002906E5 /* Preview Content */,
67CF41EC2C41C5DF002906E5 /* TODO.md */,
);
path = "Screaming Reminder";
sourceTree = "<group>";
};
67CF41D92C3FED91002906E5 /* Preview Content */ = {
isa = PBXGroup;
children = (
67CF41DA2C3FED91002906E5 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
67CF41E12C406895002906E5 /* Utils */ = {
isa = PBXGroup;
children = (
67CF41E62C408E73002906E5 /* NotificationDelegate.swift */,
67CF41EA2C4157DA002906E5 /* NotificationsUtils.swift */,
);
path = Utils;
sourceTree = "<group>";
};
67E5F03B2C53CDC3005DDD7A /* Models */ = {
isa = PBXGroup;
children = (
67E5F0392C504501005DDD7A /* Extensions.swift */,
67CF41D52C3FED90002906E5 /* Notification.swift */,
67CF41E42C4080DE002906E5 /* Reminder.swift */,
);
path = Models;
sourceTree = "<group>";
};
67E5F03C2C53CDD7005DDD7A /* Views */ = {
isa = PBXGroup;
children = (
67CF41D32C3FED90002906E5 /* ContentView.swift */,
67E5F0372C4BED68005DDD7A /* SheetView.swift */,
);
path = Views;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
67CF41CD2C3FED90002906E5 /* Screaming Reminder */ = {
isa = PBXNativeTarget;
buildConfigurationList = 67CF41DE2C3FED91002906E5 /* Build configuration list for PBXNativeTarget "Screaming Reminder" */;
buildPhases = (
67CF41CA2C3FED90002906E5 /* Sources */,
67CF41CB2C3FED90002906E5 /* Frameworks */,
67CF41CC2C3FED90002906E5 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Screaming Reminder";
productName = "Screaming Reminder";
productReference = 67CF41CE2C3FED90002906E5 /* Screaming Reminder.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
67CF41C62C3FED90002906E5 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1540;
TargetAttributes = {
67CF41CD2C3FED90002906E5 = {
CreatedOnToolsVersion = 15.4;
};
};
};
buildConfigurationList = 67CF41C92C3FED90002906E5 /* Build configuration list for PBXProject "Screaming Reminder" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = fr;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
fr,
);
mainGroup = 67CF41C52C3FED90002906E5;
productRefGroup = 67CF41CF2C3FED90002906E5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
67CF41CD2C3FED90002906E5 /* Screaming Reminder */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
67CF41CC2C3FED90002906E5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67CF41DB2C3FED91002906E5 /* Preview Assets.xcassets in Resources */,
67E5F0402C53D898005DDD7A /* .gitignore in Resources */,
67CF41D82C3FED91002906E5 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
67CF41CA2C3FED90002906E5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67CF41E72C408E73002906E5 /* NotificationDelegate.swift in Sources */,
67E5F03A2C504501005DDD7A /* Extensions.swift in Sources */,
67CF41D42C3FED90002906E5 /* ContentView.swift in Sources */,
67CF41EB2C4157DA002906E5 /* NotificationsUtils.swift in Sources */,
67CF41D62C3FED90002906E5 /* Notification.swift in Sources */,
67CF41D22C3FED90002906E5 /* Screaming_ReminderApp.swift in Sources */,
67E5F0382C4BED68005DDD7A /* SheetView.swift in Sources */,
67CF41E52C4080DE002906E5 /* Reminder.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
67CF41DC2C3FED91002906E5 /* 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;
DEBUG_INFORMATION_FORMAT = dwarf;
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;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
67CF41DD2C3FED91002906E5 /* 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;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
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;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
67CF41DF2C3FED91002906E5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Screaming Reminder/Preview Content\"";
DEVELOPMENT_TEAM = SH3XZXB7R8;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "REMINDER!";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "apps.amine.bou.Screaming-Reminder";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
67CF41E02C3FED91002906E5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Screaming Reminder/Preview Content\"";
DEVELOPMENT_TEAM = SH3XZXB7R8;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "REMINDER!";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "apps.amine.bou.Screaming-Reminder";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
67CF41C92C3FED90002906E5 /* Build configuration list for PBXProject "Screaming Reminder" */ = {
isa = XCConfigurationList;
buildConfigurations = (
67CF41DC2C3FED91002906E5 /* Debug */,
67CF41DD2C3FED91002906E5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
67CF41DE2C3FED91002906E5 /* Build configuration list for PBXNativeTarget "Screaming Reminder" */ = {
isa = XCConfigurationList;
buildConfigurations = (
67CF41DF2C3FED91002906E5 /* Debug */,
67CF41E02C3FED91002906E5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 67CF41C62C3FED90002906E5 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67CF41CD2C3FED90002906E5"
BuildableName = "Screaming Reminder.app"
BlueprintName = "Screaming Reminder"
ReferencedContainer = "container:Screaming Reminder.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67CF41CD2C3FED90002906E5"
BuildableName = "Screaming Reminder.app"
BlueprintName = "Screaming Reminder"
ReferencedContainer = "container:Screaming Reminder.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "67CF41CD2C3FED90002906E5"
BuildableName = "Screaming Reminder.app"
BlueprintName = "Screaming Reminder"
ReferencedContainer = "container:Screaming Reminder.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?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>SchemeUserState</key>
<dict>
<key>Screaming Reminder.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>67CF41CD2C3FED90002906E5</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "Frame 1(1).png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,33 @@
//
// Extensions.swift
// Screaming Reminder
//
// Created by Amine Bou on 23/07/2024.
//
import Foundation
extension Date {
func withSpecificHour(hour: Int) -> Date {
var dateComponents = Calendar.current.dateComponents(in: TimeZone.current, from: self)
dateComponents.hour = hour
dateComponents.minute = 0
return Calendar.current.date(from: dateComponents).unsafelyUnwrapped
}
func atZeroMinutes() -> Date {
var dateComponents = Calendar.current.dateComponents(in: TimeZone.current, from: self)
dateComponents.minute = 0
return Calendar.current.date(from: dateComponents).unsafelyUnwrapped
}
}
extension Set {
mutating func insertAll(_ newMembers: [Set.Element]) {
newMembers.forEach { (member) in
self.insert(member)
}
}
}

View File

@ -0,0 +1,35 @@
//
// Item.swift
// Screaming Reminder
//
// Created by Amine Bou on 11/07/2024.
//
import Foundation
import SwiftUI
final class Notification: CustomStringConvertible {
var reminder: Reminder
var triggers: [UNCalendarNotificationTrigger]
init(reminder: Reminder) {
self.reminder = reminder
self.triggers = getTriggersForReminder(reminder: reminder)
}
public var description: String { return "\(reminder) + \(triggers)" }
}
func getTriggersForReminder(reminder: Reminder) -> [UNCalendarNotificationTrigger] {
return reminder.when.map {
let components = NSCalendar.current.dateComponents([.second, .minute, .hour], from: $0)
return UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
}
}
func getIdentifier(reminder: Reminder, trigger: UNCalendarNotificationTrigger) -> String {
return "\(reminder.label)-\(trigger.dateComponents.hour ?? -1)-\(trigger.dateComponents.minute ?? -1)"
}

View File

@ -0,0 +1,33 @@
//
// Reminder.swift
// Screaming Reminder
//
// Created by Amine Bou on 11/07/2024.
//
import SwiftUI
import SwiftData
@Model
final class Reminder: CustomStringConvertible {
var label: String
var when: [Date]
required init(label: String, when: [Date]) {
self.label = label
self.when = when
}
public var description: String {
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.timeStyle = .short
return formatter
}()
let smallestDate = self.when.sorted()[0]
return "\(label) - Tous les jours, à partir de \(dateFormatter.string(from: smallestDate))"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,64 @@
//
// Screaming_ReminderApp.swift
// Screaming Reminder
//
// Created by Amine Bou on 11/07/2024.
//
import SwiftUI
import SwiftData
@main
struct Screaming_ReminderApp: App {
private var notificDelegate : NotificationDelegate = NotificationDelegate()
let modelContainer: ModelContainer
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(modelContainer)
}
init() {
do {
let schema = Schema([
Reminder.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
registerForNotification()
// Define the custom actions.
let doneAction = UNNotificationAction(identifier: "DONE_ACTION",
title: "Fait !",
options: [])
let notificationCategory =
UNNotificationCategory(identifier: "CAT",
actions: [doneAction],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: "",
options: .customDismissAction)
// Register the reminder type.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.setNotificationCategories([notificationCategory])
UNUserNotificationCenter.current().delegate = notificDelegate
}
func registerForNotification() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
} else {
exit(0)
}
}
}
}

View File

@ -0,0 +1,44 @@
//
// NotificationDelegate.swift
// Screaming Reminder
//
// Created by Amine Bou on 12/07/2024.
//
import Foundation
import SwiftUI
import SwiftData
class NotificationDelegate: NSObject , UNUserNotificationCenterDelegate{
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler:
@escaping () -> Void) {
let title = response.notification.request.content.title
switch response.actionIdentifier {
case "DONE_ACTION":
let content = UNMutableNotificationContent()
content.title = "Bravo !"
content.subtitle = "Tu as fais ce que tu devais faire ! (Il faudra ignorer les autres notifications 😅)"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
// choose a random identifier
let request = UNNotificationRequest(identifier: "\(title)-done", content: content, trigger: trigger)
// add our reminder request
UNUserNotificationCenter.current().add(request)
break
default:
exit(0)
break
}
// Always call the completion handler when done.
completionHandler()
}
}

View File

@ -0,0 +1,35 @@
//
// NotificationsUtils.swift
// Screaming Reminder
//
// Created by Amine Bou on 12/07/2024.
//
import Foundation
import SwiftUI
import SwiftData
func scheduleNotifications(reminder: Reminder) {
let notification = Notification(reminder: reminder)
notification.triggers.forEach {
let content = UNMutableNotificationContent()
content.title = notification.reminder.label
content.sound = UNNotificationSound.defaultCritical
content.categoryIdentifier = "CAT"
let request = UNNotificationRequest(identifier: getIdentifier(reminder: reminder, trigger: $0), content: content, trigger: $0)
// add our reminder request
UNUserNotificationCenter.current().add(request)
}
}
func cancelNotifications(reminder: Reminder) {
let identifiers = getTriggersForReminder(reminder: reminder).map {
getIdentifier(reminder: reminder, trigger: $0)
}
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)
}

View File

@ -0,0 +1,90 @@
//
// ContentView.swift
// Screaming Reminder
//
// Created by Amine Bou on 11/07/2024.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Query private var reminders: [Reminder]
@State var shouldPresentSheet = false
@State var selectedLabel: String = ""
@State var selectedReminder: Reminder?
@State var selectedDate = [Date()]
@State var showingAlert: Bool = false
var body: some View {
NavigationSplitView {
List {
ForEach(reminders) { reminder in
Button {
showModal(reminder: reminder)
} label: {
Text("\(reminder)")
}
}
}
.toolbar {
ToolbarItem {
Button {
showModal()
} label : {
Label("Add Item", systemImage: "plus")
}
}
#if targetEnvironment(simulator)
ToolbarItem {
Button(action: testNotifs) {
Label("Test notifs", systemImage: "volume")
}
}
ToolbarItem {
Button(action: {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}) {
Label("Delete", systemImage: "speaker.slash")
}
}
#endif
}.sheet(isPresented: $shouldPresentSheet) {
selectedDate = [Date()]
selectedReminder = nil
selectedLabel = ""
} content: {
SheetView(selectedLabel: $selectedLabel, selectedReminder: $selectedReminder, selectedDate: $selectedDate)
}
} detail: {
Text("Select a reminder")
}
}
private func testNotifs() {
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: {
for notification in $0 {
print("\(notification.identifier) - \(notification.trigger!) \((notification.trigger! as! UNCalendarNotificationTrigger).nextTriggerDate())")
}
})
}
private func showModal(reminder: Reminder? = nil) {
withAnimation {
if (reminder != nil) {
selectedReminder = reminder
selectedLabel = reminder!.label
selectedDate = reminder!.when
}
shouldPresentSheet = true
}
}
}
#Preview {
ContentView()
.modelContainer(for: Reminder.self, inMemory: true)
}

View File

@ -0,0 +1,96 @@
//
// SheetView.swift
// Screaming Reminder
//
// Created by Amine Bou on 20/07/2024.
//
import SwiftUI
import SwiftData
struct SheetView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Binding var selectedLabel: String
@Binding var selectedReminder: Reminder?
@Binding var selectedDate: [Date]
var body: some View {
Form {
LabeledContent {
TextField("", text: $selectedLabel)
} label: {
Text("Nom")
}
VStack {
ForEach($selectedDate.indices, id: \.self) { i in
HStack {
DatePicker("Quand", selection: $selectedDate[i], displayedComponents: .hourAndMinute)
Button(action: {
selectedDate.remove(at: i)
}) {
Label("", systemImage: "minus.circle.fill")
}.buttonStyle(.borderless)
.disabled(selectedDate.count == 1)
}
}
}
Button(action: {
selectedDate.append(selectedDate.last!)
}) {
Label("Ajouter une notification", systemImage: "plus")
}.buttonStyle(.borderless)
HStack {
Button("Cancel") {
dismiss()
}
if ($selectedReminder.wrappedValue != nil) {
Spacer()
Button("Supprimer") {
deleteReminder()
dismiss()
}.buttonStyle(.bordered)
}
Spacer()
Button("Confirm") {
upsertReminder()
dismiss()
}.buttonStyle(.borderedProminent)
}
}.formStyle(.grouped)
.textFieldStyle(.roundedBorder)
}
private func deleteReminder() {
withAnimation {
cancelNotifications(reminder: $selectedReminder.wrappedValue!)
modelContext.delete($selectedReminder.wrappedValue!)
}
}
private func upsertReminder() {
withAnimation {
let reminder: Reminder
if (selectedReminder != nil) {
cancelNotifications(reminder: $selectedReminder.wrappedValue!)
// Should i Wait ?
selectedReminder!.label = $selectedLabel.wrappedValue
selectedReminder!.when = $selectedDate.wrappedValue
reminder = selectedReminder!
} else {
reminder = Reminder(label: $selectedLabel.wrappedValue, when: $selectedDate.wrappedValue)
modelContext.insert(reminder)
}
scheduleNotifications(reminder: reminder)
}
}
}

8
TODO.md Normal file
View File

@ -0,0 +1,8 @@
# TODO
- Au lieu de virer toutes les notifs, ignorer les suivantes aujourd'hui
# Later
- TimeDiffs manuelles