Skip to content
Open
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
24 changes: 16 additions & 8 deletions CouponWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
54A74DE32D7E779200BCBE47 /* Exceptions for "CouponWallet" folder in "CouponWallet" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
info.plist,
Resources/info.plist,
);
target = 549BDD392D79259100B64F07 /* CouponWallet */;
};
Expand Down Expand Up @@ -91,7 +91,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1620;
LastUpgradeCheck = 1620;
LastUpgradeCheck = 1640;
TargetAttributes = {
549BDD392D79259100B64F07 = {
CreatedOnToolsVersion = 16.2;
Expand Down Expand Up @@ -173,6 +173,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 5R33532VPH;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand Down Expand Up @@ -236,6 +237,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 5R33532VPH;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand Down Expand Up @@ -264,8 +266,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"CouponWallet/Preview Content\"";
DEVELOPMENT_TEAM = 5R33532VPH;
DEVELOPMENT_ASSET_PATHS = "\"CouponWallet/Resources/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = CouponWallet;
Expand All @@ -283,9 +284,13 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = kr.co.youngmin.CouponWallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
Expand All @@ -296,8 +301,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"CouponWallet/Preview Content\"";
DEVELOPMENT_TEAM = 5R33532VPH;
DEVELOPMENT_ASSET_PATHS = "\"CouponWallet/Resources/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = CouponWallet;
Expand All @@ -315,9 +319,13 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = kr.co.youngmin.CouponWallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ class Gifticon {
var productName: String
var imagePath: String
var isUsed: Bool
var category: String

init(brand: String, productName: String, expirationDate: Date, isUsed: Bool, imagePath: String) {
init(brand: String, productName: String, expirationDate: Date, isUsed: Bool, imagePath: String, category: String) {
self.id = UUID()
self.brand = brand
self.productName = productName
self.expirationDate = expirationDate
self.isUsed = isUsed
self.imagePath = imagePath
self.category = category
}

var formattedExpiryDate: String {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "AppIcon.jpg",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct ScanResult {
var expirationDate: Date = TextAnalyzer.defaultExpirationDate
var imagePath: String = ""
var imageData: Data? = nil
var category: String = "기타"
}

struct TextAnalyzer {
Expand Down Expand Up @@ -418,27 +419,47 @@ class GifticonScanManager: ObservableObject {

// MARK: - 브랜드 추출 (개선됨)
private func extractBrand(from texts: [String], pairs: [String: String]) {
// 1. 레이블-값 쌍에서 브랜드 찾기
if let exchange = pairs["교환처"], !exchange.isEmpty {
scanResult.brand = exchange
scanResult.category = category(for: exchange)
return
}

// 2. 텍스트에서 브랜드 키워드 찾기
for text in texts {
for brand in brandKeywords {
if text.lowercased().contains(brand.lowercased()) {
scanResult.brand = brand
scanResult.category = category(for: brand)
return
}
}
}

// 3. 브랜드를 찾지 못했을 경우 기본값 설정

if scanResult.brand.isEmpty {
scanResult.brand = "기타"
scanResult.category = "기타"
} else {
scanResult.category = category(for: scanResult.brand)
}
}

// 브랜드명에 따른 카테고리 반환
private func category(for brand: String) -> String {
let convenienceStores = ["CU", "GS25", "세븐일레븐"]
let cafes = ["스타벅스", "이디야", "투썸플레이스", "빽다방", "메가커피"]
let chickenBrands = ["BBQ", "BHC", "교촌", "굽네치킨", "네네치킨"]

if convenienceStores.contains(where: { brand.localizedCaseInsensitiveContains($0) }) {
return "편의점"
} else if cafes.contains(where: { brand.localizedCaseInsensitiveContains($0) }) {
return "카페"
} else if chickenBrands.contains(where: { brand.localizedCaseInsensitiveContains($0) }) {
return "치킨"
} else {
return "기타"
}
}


// MARK: - 유효기간 추출
private func extractExpirationDate(from texts: [String], pairs: [String: String]) {
Expand Down Expand Up @@ -609,3 +630,36 @@ extension UIView {
}
}
}

extension AvailableGifticonView {
// 이미지를 Documents 디렉토리에 저장하고 파일명 반환
private func saveImageToDocuments(_ image: UIImage) -> String? {
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return nil }

// Documents 디렉토리 경로 가져오기
let documentsPath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]

// 유니크한 파일명 생성
let fileName = UUID().uuidString + ".jpg"
let fileURL = documentsPath.appendingPathComponent(fileName)

do {
try imageData.write(to: fileURL)
return fileName // 파일명만 반환 (전체 경로 아님)
} catch {
print("이미지 저장 실패: \(error)")
return nil
}
}

// 수정된 이미지 저장 및 경로 반환 함수
private func saveImageAndGetPath(_ image: UIImage, manager: GifticonScanManager) -> String {
// Documents 디렉토리에 저장
if let fileName = saveImageToDocuments(image) {
return fileName
}
return ""
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ struct ScanResultView: View {
productName: productName,
expirationDate: expirationDate,
isUsed: false,
imagePath: imagePath
imagePath: imagePath,
category: scanManager.scanResult.category
)

modelContext.insert(newGifticon)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ enum GifticonType {
// 사용 가능한 기프티콘 뷰
struct AvailableGifticonView: View {
@State private var selectedFilter = "전체"
let filters = ["전체", "스타벅스", "치킨", "CU", "GS25", "기타"]
let filters = ["전체", "편의점", "카페", "치킨", "기타"]

@Query private var availableGifticons: [Gifticon]
@Environment(\.modelContext) private var modelContext
Expand Down Expand Up @@ -81,10 +81,11 @@ struct AvailableGifticonView: View {

// 필터링 로직을 별도 함수로 분리
private func getFilteredGifticons() -> [Gifticon] {
if selectedFilter == "전체" {
return availableGifticons
} else {
return availableGifticons.filter { $0.brand == selectedFilter }
switch selectedFilter {
case "전체":
return availableGifticons
default:
return availableGifticons.filter { $0.category == selectedFilter }
}
}

Expand Down Expand Up @@ -329,7 +330,8 @@ struct AvailableGifticonView: View {
productName: scanManager.scanResult.productName,
expirationDate: scanManager.scanResult.expirationDate,
isUsed: false,
imagePath: imagePath
imagePath: imagePath,
category: scanManager.scanResult.category
)

modelContext.insert(newGifticon)
Expand All @@ -343,7 +345,8 @@ struct AvailableGifticonView: View {
productName: "기프티콘",
expirationDate: Date().addingTimeInterval(30*24*60*60), // 30일 후 만료
isUsed: false,
imagePath: imagePath
imagePath: imagePath,
category: "기타"
)

modelContext.insert(newGifticon)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,28 +198,46 @@ struct GifticonCard: View {
let status: String?
@Environment(\.colorScheme) var colorScheme

// 로컬 이미지 로드 함수
private func loadLocalImage(from fileName: String) -> UIImage? {
guard !fileName.isEmpty else { return nil }

let documentsPath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
let fileURL = documentsPath.appendingPathComponent(fileName)

if FileManager.default.fileExists(atPath: fileURL.path) {
return UIImage(contentsOfFile: fileURL.path)
}
return nil
}

var body: some View {
VStack(alignment: .leading, spacing: 8) {
ZStack {
// 이미지 표시 로직 수정
if !gifticon.imagePath.isEmpty {
AsyncImage(url: URL(string: gifticon.imagePath)) { image in
image.resizable()
// 로컬 이미지 먼저 시도
if let localImage = loadLocalImage(from: gifticon.imagePath) {
Image(uiImage: localImage)
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
Color.gray.opacity(0.1)
.frame(height: 100)
} else {
// URL 이미지 시도 (기존 코드)
AsyncImage(url: URL(string: gifticon.imagePath)) { image in
image.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
defaultImagePlaceholder
}
.frame(height: 100)
}
.frame(height: 100)
} else {
Rectangle()
.fill(Color.gray.opacity(0.1))
.frame(height: 100)
.overlay(
Text(gifticon.brand)
.foregroundColor(.gray)
)
defaultImagePlaceholder
}

// "available"이 아닐 때만 상태 표시
// 상태 표시 (기존 코드와 동일)
if let status = status, status != "available" && !status.isEmpty {
Text(status)
.font(.caption)
Expand Down Expand Up @@ -248,6 +266,17 @@ struct GifticonCard: View {
.cornerRadius(12)
.shadow(color: colorScheme == .dark ? Color.clear : Color.black.opacity(0.05), radius: 5, x: 0, y: 2)
}

// 기본 이미지 플레이스홀더
private var defaultImagePlaceholder: some View {
Rectangle()
.fill(Color.gray.opacity(0.1))
.frame(height: 100)
.overlay(
Text(gifticon.brand)
.foregroundColor(.gray)
)
}
}

extension Int {
Expand Down
Loading