Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to DotoryCount are recorded in this file.

## [0.5.0] - 2026-09-26

- Apply Liquid Glass to the dashboard action and information panel on iOS 26+,
with material fallbacks on older supported iOS versions.
- Add a compact rectangular Lock Screen day counter and a small Home Screen
widget with the photographic jar, staged acorn pile, and D+ counter.
- Share the current anniversary summary with WidgetKit and refresh its timeline
after edits, deletion, and the next calendar day.
- Bumped the app and widget to version 0.5.0 (build 11).

## [0.4.2] - 2026-09-24

- Keep the photographed glass floor behind the acorns while retaining the
Expand Down
14 changes: 14 additions & 0 deletions Configuration/WidgetInfo.plist
Original file line number Diff line number Diff line change
@@ -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>CFBundleDevelopmentRegion</key><string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key><string>도토리 카운트</string>
<key>CFBundleExecutable</key><string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key><string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
<key>CFBundleName</key><string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key><string>XPC!</string>
<key>CFBundleShortVersionString</key><string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key><string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key><dict><key>NSExtensionPointIdentifier</key><string>com.apple.widgetkit-extension</string></dict>
</dict></plist>
79 changes: 73 additions & 6 deletions DotoryCount.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
objectVersion = 77;
objects = {

/* Begin PBXBuildFile section */
D05000000000000000000001 /* DotoryCountWidget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D05000000000000000000002 /* DotoryCountWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
D0500000000000000000000B /* PBXContainerItemProxy */ = {isa = PBXContainerItemProxy; containerPortal = A607FF8D3055937D00D23132 /* Project object */; proxyType = 1; remoteGlobalIDString = D05000000000000000000005; remoteInfo = DotoryCountWidget; };
A607FFA33055937F00D23132 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A607FF8D3055937D00D23132 /* Project object */;
Expand All @@ -23,13 +28,19 @@
};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
D05000000000000000000008 /* Embed App Extensions */ = {isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = (D05000000000000000000001 /* DotoryCountWidget.appex in Embed App Extensions */); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; };
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
D05000000000000000000002 /* DotoryCountWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DotoryCountWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
A607FF953055937D00D23132 /* DotoryCount.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DotoryCount.app; sourceTree = BUILT_PRODUCTS_DIR; };
A607FFA23055937F00D23132 /* DotoryCountTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DotoryCountTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A607FFAC3055937F00D23132 /* DotoryCountUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DotoryCountUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
D05000000000000000000003 /* DotoryCountWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; path = DotoryCountWidget; sourceTree = "<group>"; };
A607FF973055937D00D23132 /* DotoryCount */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = DotoryCount;
Expand All @@ -48,6 +59,7 @@
/* End PBXFileSystemSynchronizedRootGroup section */

/* Begin PBXFrameworksBuildPhase section */
D05000000000000000000004 /* Frameworks */ = {isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; };
A607FF923055937D00D23132 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -76,6 +88,7 @@
isa = PBXGroup;
children = (
A607FF973055937D00D23132 /* DotoryCount */,
D05000000000000000000003 /* DotoryCountWidget */,
A607FFA53055937F00D23132 /* DotoryCountTests */,
A607FFAF3055937F00D23132 /* DotoryCountUITests */,
A607FF963055937D00D23132 /* Products */,
Expand All @@ -86,6 +99,7 @@
isa = PBXGroup;
children = (
A607FF953055937D00D23132 /* DotoryCount.app */,
D05000000000000000000002 /* DotoryCountWidget.appex */,
A607FFA23055937F00D23132 /* DotoryCountTests.xctest */,
A607FFAC3055937F00D23132 /* DotoryCountUITests.xctest */,
);
Expand All @@ -95,18 +109,31 @@
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
D05000000000000000000005 /* DotoryCountWidget */ = {
isa = PBXNativeTarget;
buildConfigurationList = D05000000000000000000009 /* Build configuration list for PBXNativeTarget "DotoryCountWidget" */;
buildPhases = (D05000000000000000000006 /* Sources */, D05000000000000000000004 /* Frameworks */, D05000000000000000000007 /* Resources */);
buildRules = ();
dependencies = ();
fileSystemSynchronizedGroups = (D05000000000000000000003 /* DotoryCountWidget */);
name = DotoryCountWidget;
packageProductDependencies = ();
productName = DotoryCountWidget;
productReference = D05000000000000000000002 /* DotoryCountWidget.appex */;
productType = "com.apple.product-type.app-extension";
};
A607FF943055937D00D23132 /* DotoryCount */ = {
isa = PBXNativeTarget;
buildConfigurationList = A607FFB63055937F00D23132 /* Build configuration list for PBXNativeTarget "DotoryCount" */;
buildPhases = (
A607FF913055937D00D23132 /* Sources */,
A607FF923055937D00D23132 /* Frameworks */,
A607FF933055937D00D23132 /* Resources */,
D05000000000000000000008 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
);
dependencies = (D0500000000000000000000A /* PBXTargetDependency */);
fileSystemSynchronizedGroups = (
A607FF973055937D00D23132 /* DotoryCount */,
);
Expand Down Expand Up @@ -173,6 +200,7 @@
LastSwiftUpdateCheck = 2660;
LastUpgradeCheck = 2700;
TargetAttributes = {
D05000000000000000000005 = { CreatedOnToolsVersion = 26.6; };
A607FF943055937D00D23132 = {
CreatedOnToolsVersion = 26.6;
};
Expand Down Expand Up @@ -201,13 +229,15 @@
projectRoot = "";
targets = (
A607FF943055937D00D23132 /* DotoryCount */,
D05000000000000000000005 /* DotoryCountWidget */,
A607FFA13055937F00D23132 /* DotoryCountTests */,
A607FFAB3055937F00D23132 /* DotoryCountUITests */,
);
};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
D05000000000000000000007 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; };
A607FF933055937D00D23132 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -232,6 +262,7 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
D05000000000000000000006 /* Sources */ = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; };
A607FF913055937D00D23132 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -256,6 +287,7 @@
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
D0500000000000000000000A /* PBXTargetDependency */ = {isa = PBXTargetDependency; target = D05000000000000000000005 /* DotoryCountWidget */; targetProxy = D0500000000000000000000B /* PBXContainerItemProxy */; };
A607FFA43055937F00D23132 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A607FF943055937D00D23132 /* DotoryCount */;
Expand All @@ -269,6 +301,38 @@
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
D0500000000000000000000C /* Debug */ = {isa = XCBuildConfiguration; buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DotoryCountWidget/DotoryCountWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_TEAM = W395S42ZJ9;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Configuration/WidgetInfo.plist;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = com.dotoryman.dotorycount.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
}; name = Debug; };
D0500000000000000000000D /* Release */ = {isa = XCBuildConfiguration; buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DotoryCountWidget/DotoryCountWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_TEAM = W395S42ZJ9;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Configuration/WidgetInfo.plist;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = com.dotoryman.dotorycount.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
}; name = Release; };
A607FFB43055937F00D23132 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
Expand Down Expand Up @@ -393,10 +457,11 @@
A607FFB73055937F00D23132 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = DotoryCount/DotoryCount.entitlements;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_TEAM = W395S42ZJ9;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -409,7 +474,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.2;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = com.dotoryman.dotorycount;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
Expand All @@ -425,10 +490,11 @@
A607FFB83055937F00D23132 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = DotoryCount/DotoryCount.entitlements;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 11;
DEVELOPMENT_TEAM = W395S42ZJ9;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -441,7 +507,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.2;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = com.dotoryman.dotorycount;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
Expand Down Expand Up @@ -537,6 +603,7 @@
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
D05000000000000000000009 /* Build configuration list for PBXNativeTarget "DotoryCountWidget" */ = {isa = XCConfigurationList; buildConfigurations = (D0500000000000000000000C /* Debug */, D0500000000000000000000D /* Release */); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; };
A607FF903055937D00D23132 /* Build configuration list for PBXProject "DotoryCount" */ = {
isa = XCConfigurationList;
buildConfigurations = (
Expand Down
16 changes: 16 additions & 0 deletions DotoryCount/Core/Persistence/WidgetSnapshotStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation
import WidgetKit

/// Only the small, non-sensitive summary a widget needs crosses the app boundary.
enum WidgetSnapshotStore {
static let suiteName = "group.com.dotoryman.dotorycount"
private static let titleKey = "widget.title"
private static let startDateKey = "widget.startDate"

static func publish(_ anniversary: Anniversary?) {
guard let defaults = UserDefaults(suiteName: suiteName) else { return }
defaults.set(anniversary?.title, forKey: titleKey)
defaults.set(anniversary?.startDate, forKey: startDateKey)
WidgetCenter.shared.reloadAllTimelines()
}
}
41 changes: 41 additions & 0 deletions DotoryCount/DesignSystem/GlassStyles.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI

struct GlassActionStyle: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 26.0, *) {
content.buttonStyle(.glass)
} else {
content.background(.regularMaterial, in: Circle())
}
}
}

struct GlassInformationPanel: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 26.0, *) {
content.glassEffect(.regular, in: .rect(cornerRadius: 19))
} else {
content.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 19))
}
}
}

struct GlassPrimaryActionStyle: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 26.0, *) {
content.buttonStyle(.glassProminent)
} else {
content.buttonStyle(.borderedProminent)
}
}
}

struct GlassSecondaryActionStyle: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 26.0, *) {
content.buttonStyle(.glass)
} else {
content.buttonStyle(.bordered)
}
}
}
3 changes: 3 additions & 0 deletions DotoryCount/DotoryCount.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?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.application-groups</key><array><string>group.com.dotoryman.dotorycount</string></array></dict></plist>
8 changes: 7 additions & 1 deletion DotoryCount/Features/Editor/AnniversaryEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,19 @@ struct AnniversaryEditorView: View {

private func save() {
do {
let savedAnniversary: Anniversary
if let anniversary {
anniversary.title = trimmedTitle
anniversary.startDate = startDate
savedAnniversary = anniversary
} else {
modelContext.insert(Anniversary(title: trimmedTitle, startDate: startDate))
let newAnniversary = Anniversary(title: trimmedTitle, startDate: startDate)
modelContext.insert(newAnniversary)
savedAnniversary = newAnniversary
}

try modelContext.save()
WidgetSnapshotStore.publish(savedAnniversary)
dismiss()
} catch {
modelContext.rollback()
Expand All @@ -153,6 +158,7 @@ struct AnniversaryEditorView: View {
do {
modelContext.delete(anniversary)
try modelContext.save()
WidgetSnapshotStore.publish(nil)
dismiss()
} catch {
modelContext.rollback()
Expand Down
2 changes: 1 addition & 1 deletion DotoryCount/Features/Editor/StartDateSelectionSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct StartDateSelectionSheet: View {
preset("어제", daysAgo: 1)
preset("1년 전", yearsAgo: 1)
}
.buttonStyle(.bordered)
.modifier(GlassSecondaryActionStyle())
.listRowBackground(Color.clear)
}

Expand Down
4 changes: 2 additions & 2 deletions DotoryCount/Features/Home/CinematicAnniversaryDashboard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ struct CinematicAnniversaryDashboard: View {
Image(systemName: "ellipsis")
.font(.system(size: 19, weight: .medium))
.frame(width: 42, height: 42)
.background(.white.opacity(0.86), in: Circle())
}
.modifier(GlassActionStyle())
.accessibilityLabel("기념일 수정")
.accessibilityIdentifier("edit-anniversary-button")
}
Expand Down Expand Up @@ -134,7 +134,7 @@ struct CinematicAnniversaryDashboard: View {
.foregroundStyle(AppTheme.secondaryText)
}
.padding(15)
.background(.white.opacity(0.88), in: RoundedRectangle(cornerRadius: 19))
.modifier(GlassInformationPanel())
}

private func replay() {
Expand Down
12 changes: 11 additions & 1 deletion DotoryCount/Features/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ struct HomeView: View {
guard previousSheet != nil, currentSheet == nil else { return }
dashboardReplayToken &+= 1
}
.onAppear { WidgetSnapshotStore.publish(anniversaries.first) }
.onChange(of: anniversaries.count) { _, _ in
WidgetSnapshotStore.publish(anniversaries.first)
}
.onChange(of: anniversaries.first?.startDate) { _, _ in
WidgetSnapshotStore.publish(anniversaries.first)
}
.onChange(of: anniversaries.first?.title) { _, _ in
WidgetSnapshotStore.publish(anniversaries.first)
}
}
}

Expand Down Expand Up @@ -84,7 +94,7 @@ private struct EmptyAnniversaryView: View {
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
}
.buttonStyle(.borderedProminent)
.modifier(GlassPrimaryActionStyle())
.buttonBorderShape(.roundedRectangle(radius: 16))
.accessibilityIdentifier("create-anniversary-button")

Expand Down
Loading