diff --git a/iOS-NOTTODO/Settings.bundle/Root.plist b/iOS-NOTTODO/Settings.bundle/Root.plist
index 102380b9..71527a74 100644
--- a/iOS-NOTTODO/Settings.bundle/Root.plist
+++ b/iOS-NOTTODO/Settings.bundle/Root.plist
@@ -2,6 +2,8 @@
+ ApplicationGroupContainerIdentifier
+ group.nottodo.iOS-NOTTODO
StringsTable
Root
PreferenceSpecifiers
diff --git a/iOS-NOTTODO/Widget-NOTTODO/AppIntent.swift b/iOS-NOTTODO/Widget-NOTTODO/AppIntent.swift
new file mode 100644
index 00000000..f7be937d
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/AppIntent.swift
@@ -0,0 +1,34 @@
+//
+// AppIntent.swift
+// Widget-NOTTODO
+//
+// Created by 강윤서 on 4/14/24.
+//
+
+import WidgetKit
+import AppIntents
+
+struct ConfigurationAppIntent: WidgetConfigurationIntent {
+ static var title: LocalizedStringResource = "Configuration"
+}
+
+struct ToggleButtonIntent: AppIntent {
+ static var title: LocalizedStringResource = .init(stringLiteral: "Mission's State")
+
+ @Parameter(title: "Mission ID")
+ var id: Int
+
+ @Parameter(title: "Mission status")
+ var status: String
+
+ init() { }
+ init(id: Int, status: String) {
+ self.id = id
+ self.status = status == CompletionStatus.UNCHECKED.rawValue ? CompletionStatus.CHECKED.rawValue : CompletionStatus.UNCHECKED.rawValue
+ }
+
+ func perform() async throws -> some IntentResult {
+ _ = try await WidgetService.shared.updateMission(id: id, status: status)
+ return .result()
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Global/Extensions/Formatter.swift b/iOS-NOTTODO/Widget-NOTTODO/Global/Extensions/Formatter.swift
new file mode 100644
index 00000000..2ba9253b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Global/Extensions/Formatter.swift
@@ -0,0 +1,18 @@
+//
+// Formatter.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/12/24.
+//
+
+import Foundation
+
+struct Formatter {
+ static func dateFormatterString(format: String?, date: Date) -> String {
+ let formatter = Foundation.DateFormatter()
+ formatter.dateFormat = format ?? "yyyy-MM-dd"
+ formatter.locale = Locale(identifier: "ko_KR")
+ let convertStr = formatter.string(from: date)
+ return convertStr
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Network/Base/NetworkError.swift b/iOS-NOTTODO/Widget-NOTTODO/Network/Base/NetworkError.swift
new file mode 100644
index 00000000..3a051add
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Network/Base/NetworkError.swift
@@ -0,0 +1,17 @@
+//
+// NetworkError.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/11/24.
+//
+
+import Foundation
+
+enum NetworkError: Error {
+ case invalidResponse
+ case networkError
+ case dataParsingError
+ case invalidRequestParameters
+ case encodingFailed
+ case internalError(message: String)
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Network/DataModel/QuoteResponseDTO.swift b/iOS-NOTTODO/Widget-NOTTODO/Network/DataModel/QuoteResponseDTO.swift
new file mode 100644
index 00000000..f2b0b99b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Network/DataModel/QuoteResponseDTO.swift
@@ -0,0 +1,14 @@
+//
+// QuoteResponseDTO.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/10/24.
+//
+
+import Foundation
+
+struct QuoteResponseDTO: Codable {
+ let id: Int
+ let description: String
+ let author: String
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Network/Service/WidgetService.swift b/iOS-NOTTODO/Widget-NOTTODO/Network/Service/WidgetService.swift
new file mode 100644
index 00000000..4cff900e
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Network/Service/WidgetService.swift
@@ -0,0 +1,205 @@
+//
+// WidgetService.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/10/24.
+//
+
+import Foundation
+import SwiftUI
+
+typealias QuoteData = GeneralResponse
+typealias DailyMissionData = GeneralArrayResponse
+typealias UpdateMissionStatus = GeneralResponse
+
+struct WidgetService {
+ @AppStorage(DefaultKeys.accessToken, store: UserDefaults.shared) var accessToken: String = ""
+
+ static let shared = WidgetService()
+ private init() {}
+
+ let session = URLSession(configuration: URLSessionConfiguration.default, delegate: URLSessionLoggingDelegate(), delegateQueue: nil)
+
+ func fetchWiseSaying() async throws -> QuoteResponseDTO {
+ do {
+ return try await getWiseSaying(
+ from: generateURL(constant: URLConstant.quote))
+ } catch {
+ switch error {
+ case NetworkError.networkError:
+ print("네트워크 에러가 발생")
+ case NetworkError.invalidResponse:
+ print("유효하지 않은 응답")
+ case NetworkError.dataParsingError:
+ print("데이터 파싱 실패")
+ default:
+ print("알 수 없는 에러 발생")
+ }
+ throw error
+ }
+ }
+
+ func fetchDailyMissoin(date: String) async throws -> [DailyMissionResponseDTO] {
+ do {
+ return try await getDailyMission(
+ from: generateURL(constant: URLConstant.recommend + URLConstant.dailyMission, parameter: date))
+ } catch {
+ switch error {
+ case NetworkError.networkError:
+ print("네트워크 에러가 발생")
+ case NetworkError.invalidResponse:
+ print("유효하지 않은 응답")
+ case NetworkError.dataParsingError:
+ print("데이터 파싱 실패")
+ default:
+ print("알 수 없는 에러 발생")
+ }
+ throw error
+ }
+ }
+
+ func updateMission(id: Int, status: String) async throws {
+ do {
+ try await patchUpdateMission(
+ from: generateURL(constant: URLConstant.recommend, parameter: "\(id)" + "/check"),
+ id: id,
+ requestData: status)
+
+ let dailyMission = try await fetchDailyMissoin(date: Formatter.dateFormatterString(format: nil, date: Date()))
+ UserDefaults.shared?.setSharedCustomArray(dailyMission, forKey: "dailyMission")
+ } catch {
+ switch error {
+ case NetworkError.networkError:
+ print("네트워크 에러가 발생")
+ case NetworkError.invalidResponse:
+ print("유효하지 않은 응답")
+ case NetworkError.dataParsingError:
+ print("데이터 파싱 실패")
+ case NetworkError.encodingFailed:
+ print("데이터 인코딩 실패")
+ case NetworkError.invalidRequestParameters:
+ print("유효하지 않은 파라미터")
+ default:
+ print("알 수 없는 에러 발생")
+ }
+ throw error
+ }
+ }
+
+ private func getWiseSaying(from url: URL) async throws -> QuoteResponseDTO {
+ let (data, response) = try await session.data(from: url)
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw NetworkError.networkError
+ }
+
+ guard (200..<300).contains(httpResponse.statusCode) else {
+ throw NetworkError.invalidResponse
+ }
+
+ do {
+ let result = try JSONDecoder().decode(QuoteData.self, from: data)
+ guard let responseData = result.data else {
+ throw NetworkError.dataParsingError
+ }
+ return responseData
+ } catch {
+ throw NetworkError.dataParsingError
+ }
+ }
+
+ private func getDailyMission(from url: URL) async throws -> [DailyMissionResponseDTO] {
+ var request = URLRequest(url: url)
+ request.setValue("application/json",
+ forHTTPHeaderField: "Content-Type")
+ request.setValue(accessToken,
+ forHTTPHeaderField: "Authorization")
+
+ let (data, response) = try await session.data(for: request)
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw NetworkError.networkError
+ }
+
+ print("Response Status Code: \(httpResponse.statusCode)")
+ print("Response Headers: \(httpResponse.allHeaderFields)")
+
+ guard (200..<300).contains(httpResponse.statusCode) else {
+ throw NetworkError.invalidResponse
+ }
+
+ do {
+ let result = try JSONDecoder().decode(DailyMissionData.self, from: data)
+ guard let responseData = result.data else {
+ throw NetworkError.dataParsingError
+ }
+ return responseData
+ } catch {
+ throw NetworkError.dataParsingError
+ }
+ }
+
+ private func patchUpdateMission(from url: URL, id: Int, requestData: String) async throws {
+ var request = URLRequest(url: url)
+ request.httpMethod = "PATCH"
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.setValue(accessToken, forHTTPHeaderField: "Authorization")
+
+ do {
+ let body = ["completionStatus": requestData]
+ request.httpBody = try JSONSerialization.data(withJSONObject: body)
+
+ if let bodyData = request.httpBody, let bodyString = String(data: bodyData, encoding: .utf8) {
+ print("Request Body: \(bodyString)")
+ } else {
+ print("Request Body is empty or cannot be converted to String.")
+ }
+ } catch {
+ print("Encoding Failed: \(error)")
+ throw NetworkError.encodingFailed
+ }
+
+ do {
+ let (data, response) = try await session.data(for: request)
+ guard let httpResponse = response as? HTTPURLResponse else {
+ print("Invalid response received")
+ throw NetworkError.networkError
+ }
+
+ print("Response Status Code: \(httpResponse.statusCode)")
+ print("Response Headers: \(httpResponse.allHeaderFields)")
+
+ if (200..<300).contains(httpResponse.statusCode) {
+ if let responseString = String(data: data, encoding: .utf8) {
+ print("Response Data: \(responseString)")
+ } else {
+ print("Response data is not a valid string.")
+ }
+ do {
+ let result = try JSONDecoder().decode(UpdateMissionStatus.self, from: data)
+ guard let responseData = result.data else {
+ print("Data Parsing Error: \(String(data: data, encoding: .utf8) ?? "No data")")
+ throw NetworkError.dataParsingError
+ }
+ print("Response Data: \(responseData)")
+ } catch {
+ print("Data Parsing Error: \(error)")
+ throw NetworkError.dataParsingError
+ }
+ } else {
+ print("Invalid response status code: \(httpResponse.statusCode)")
+ throw NetworkError.invalidResponse
+ }
+ } catch {
+ print("Network Request Failed: \(error)")
+ throw NetworkError.networkError
+ }
+ }
+
+ private func generateURL(constant: String, parameter: String = "") -> URL {
+ let baseURL = Bundle.main.baseURL
+ let url = baseURL + constant + "/\(parameter)"
+ print(url)
+ return URL(string: url)!
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Provider/MissionProvider.swift b/iOS-NOTTODO/Widget-NOTTODO/Provider/MissionProvider.swift
new file mode 100644
index 00000000..9bf53dd6
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Provider/MissionProvider.swift
@@ -0,0 +1,61 @@
+//
+// MissionProvider.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/11/24.
+//
+
+import SwiftUI
+import WidgetKit
+
+struct Provider: AppIntentTimelineProvider {
+ @AppStorage("dailyMission", store: UserDefaults.shared) var sharedData: Data = Data()
+ @AppStorage("quote", store: UserDefaults.shared) var quote: String = ""
+
+ func placeholder(in context: Context) -> SimpleEntry {
+ SimpleEntry(todayMission: [], quote: quote)
+ }
+
+ func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry {
+ do {
+ try await getQuote()
+ } catch {
+ return SimpleEntry(todayMission: [], quote: "")
+ }
+ return SimpleEntry(todayMission: [], quote: quote)
+ }
+
+ func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline {
+ do {
+ let now = Date()
+ let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: now) ?? now
+ let midnightTomorrow = Calendar.current.startOfDay(for: tomorrow)
+ let midnightToday = Calendar.current.startOfDay(for: now)
+
+ if now == midnightToday {
+ try await getQuote()
+ try await getDailyMission(date: Formatter.dateFormatterString(format: nil, date: now))
+ }
+
+ guard let decodedData = try? JSONDecoder().decode([DailyMissionResponseDTO].self, from: sharedData) else {
+ return Timeline(entries: [], policy: .never)
+ }
+
+ let entry = SimpleEntry(todayMission: decodedData, quote: quote)
+ return Timeline(entries: [entry], policy: .after(midnightTomorrow))
+ } catch {
+ return Timeline(entries: [], policy: .never)
+ }
+
+ }
+
+ private func getQuote() async throws {
+ let quoteResponse = try await WidgetService.shared.fetchWiseSaying()
+ quote = quoteResponse.description + " - " + quoteResponse.author
+ }
+
+ private func getDailyMission(date: String) async throws {
+ let dailyMission = try await WidgetService.shared.fetchDailyMissoin(date: date)
+ UserDefaults.shared?.setSharedCustomArray(dailyMission, forKey: "dailyMission")
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Provider/TimeEntity.swift b/iOS-NOTTODO/Widget-NOTTODO/Provider/TimeEntity.swift
new file mode 100644
index 00000000..cfbe563b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Provider/TimeEntity.swift
@@ -0,0 +1,18 @@
+//
+// TimeEntity.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 5/11/24.
+//
+
+import WidgetKit
+
+struct SimpleEntry: TimelineEntry {
+ var date: Date = .now
+
+ var dayOfWeek: String {
+ return Formatter.dateFormatterString(format: "E", date: date)
+ }
+ var todayMission: [DailyMissionResponseDTO]
+ let quote: String
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AccentColor.colorset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..13613e3e
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/WidgetBackground.colorset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/WidgetBackground.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/WidgetBackground.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/Contents.json
new file mode 100644
index 00000000..f453598f
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/Contents.json
@@ -0,0 +1,32 @@
+{
+ "images" : [
+ {
+ "filename" : "btn_medium 1.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "light"
+ }
+ ],
+ "filename" : "btn_medium.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "btn_medium_dark.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium 1.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium 1.svg
new file mode 100644
index 00000000..926e54dd
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium 1.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium.svg
new file mode 100644
index 00000000..926e54dd
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium_dark.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium_dark.svg
new file mode 100644
index 00000000..5643f511
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium.imageset/btn_medium_dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/Contents.json
new file mode 100644
index 00000000..85fa1aec
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/Contents.json
@@ -0,0 +1,32 @@
+{
+ "images" : [
+ {
+ "filename" : "btn_medium_fill 1.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "light"
+ }
+ ],
+ "filename" : "btn_medium_fill.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "btn_medium_fill_dark.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill 1.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill 1.svg
new file mode 100644
index 00000000..fd10e474
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill 1.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill.svg
new file mode 100644
index 00000000..fd10e474
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill_dark.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill_dark.svg
new file mode 100644
index 00000000..39c23f19
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_medium_fill.imageset/btn_medium_fill_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/Contents.json
new file mode 100644
index 00000000..933eda4b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "btn_small_box.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/btn_small_box.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/btn_small_box.svg
new file mode 100644
index 00000000..b22a4302
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box.imageset/btn_small_box.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/Contents.json
new file mode 100644
index 00000000..123956be
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "btn_small_box_fill.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/btn_small_box_fill.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/btn_small_box_fill.svg
new file mode 100644
index 00000000..9f2491ba
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/btn_small_box_fill.imageset/btn_small_box_fill.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/Contents.json
new file mode 100644
index 00000000..2ddabd7b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_medium_fill.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/gage_medium_fill.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/gage_medium_fill.svg
new file mode 100644
index 00000000..482c97dc
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_fill.imageset/gage_medium_fill.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/Contents.json
new file mode 100644
index 00000000..558e7d91
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_medium_half.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/gage_medium_half.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/gage_medium_half.svg
new file mode 100644
index 00000000..a4a64187
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_half.imageset/gage_medium_half.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/Contents.json
new file mode 100644
index 00000000..f94f324b
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_medium_zero.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/gage_medium_zero.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/gage_medium_zero.svg
new file mode 100644
index 00000000..34033097
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_medium_zero.imageset/gage_medium_zero.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/Contents.json
new file mode 100644
index 00000000..db096772
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_small_fill.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/gage_small_fill.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/gage_small_fill.svg
new file mode 100644
index 00000000..d181c1a2
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_fill.imageset/gage_small_fill.svg
@@ -0,0 +1,3 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/Contents.json
new file mode 100644
index 00000000..68589657
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_small_half.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/gage_small_half.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/gage_small_half.svg
new file mode 100644
index 00000000..276ce1b9
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_half.imageset/gage_small_half.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/Contents.json
new file mode 100644
index 00000000..0d412f5a
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "gage_small_zero.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/gage_small_zero.png b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/gage_small_zero.png
new file mode 100644
index 00000000..59780a05
Binary files /dev/null and b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/gage_small_zero.imageset/gage_small_zero.png differ
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/Contents.json
new file mode 100644
index 00000000..6be24e29
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/Contents.json
@@ -0,0 +1,32 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_plus.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "light"
+ }
+ ],
+ "filename" : "ic_plus 1.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "ic_plus_darkmode.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus 1.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus 1.svg
new file mode 100644
index 00000000..7a128d9c
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus 1.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus.svg
new file mode 100644
index 00000000..7a128d9c
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus_darkmode.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus_darkmode.svg
new file mode 100644
index 00000000..46002197
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus.imageset/ic_plus_darkmode.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/Contents.json b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/Contents.json
new file mode 100644
index 00000000..02e7623a
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_plus_dark.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/ic_plus_dark.svg b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/ic_plus_dark.svg
new file mode 100644
index 00000000..7a128d9c
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Assets.xcassets/ic_plus_dark.imageset/ic_plus_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Resource/Info.plist b/iOS-NOTTODO/Widget-NOTTODO/Resource/Info.plist
new file mode 100644
index 00000000..3351d0ff
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Resource/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionAttributes
+
+ IntentsSupported
+
+ ConfigurationAppIntent
+ ToggleButtonIntent
+
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+
+
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/CircularProgressBar.swift b/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/CircularProgressBar.swift
new file mode 100644
index 00000000..76ea3b11
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/CircularProgressBar.swift
@@ -0,0 +1,39 @@
+//
+// CircularProgressBar.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 4/17/24.
+//
+
+import WidgetKit
+import SwiftUI
+
+struct CircularProgressBarView: View {
+
+ @State var percent = 0.0
+ var size: CGFloat
+ var lineWidth: CGFloat
+
+ var body: some View {
+ ZStack {
+ Circle()
+ .stroke(lineWidth: lineWidth)
+ .frame(width: size, height: size)
+ .foregroundColor(.gray3)
+
+ Circle()
+ .trim(from: 0, to: 0 < percent && percent < 1 ? 0.5 : percent)
+ .stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .butt))
+ .frame(width: size, height: size)
+ .foregroundColor(.green2)
+ .rotationEffect(.degrees(-90))
+ .scaleEffect(x: -1)
+ }
+ }
+}
+
+#Preview(as: .systemSmall) {
+ Widget_NOTTODO()
+} timeline: {
+ SimpleEntry(todayMission: [], quote: "")
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/HorizontalDivider.swift b/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/HorizontalDivider.swift
new file mode 100644
index 00000000..4263cd3c
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/View/Common/Components/HorizontalDivider.swift
@@ -0,0 +1,24 @@
+//
+// HorizontalDivider.swift
+// Widget-NOTTODOExtension
+//
+// Created by 강윤서 on 4/19/24.
+//
+
+import SwiftUI
+
+struct HorizontalDivider: View {
+
+ let color: Color
+ let height: CGFloat
+
+ init(color: Color, height: CGFloat = 1) {
+ self.color = color
+ self.height = height
+ }
+
+ var body: some View {
+ color
+ .frame(height: height)
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/View/MediumFamily.swift b/iOS-NOTTODO/Widget-NOTTODO/View/MediumFamily.swift
new file mode 100644
index 00000000..dab04b4c
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/View/MediumFamily.swift
@@ -0,0 +1,86 @@
+//
+// MediumFamily.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 4/19/24.
+//
+
+import SwiftUI
+import WidgetKit
+
+struct MediumFamily: View {
+ var entry: Provider.Entry
+
+ var body: some View {
+ let progressPercent = Double(entry.todayMission.filter { $0.completionStatus == .CHECKED }.count) / Double(entry.todayMission.count)
+ HStack {
+ VStack {
+ ZStack {
+ Text(entry.dayOfWeek)
+ .foregroundStyle(entry.dayOfWeek == "일" ? .wdgRed : .ntdBlack)
+ .font(.custom("Pretendard", size: 18))
+ .fontWeight(.semibold)
+ CircularProgressBarView(percent: progressPercent, size: 42, lineWidth: 4.34)}
+ Spacer()
+ }
+ .padding(.top, 14)
+ .padding(.leading, 17)
+
+ VStack {
+ Text(entry.quote)
+ .foregroundStyle(.gray3)
+ .font(.custom("Pretendard", size: 10))
+ .fontWeight(.regular)
+ .lineLimit(2)
+ .padding(.horizontal, 12)
+ .padding(.top, 18)
+ .frame(maxWidth: .infinity, maxHeight: 51, alignment: .leading)
+
+ HorizontalDivider(color: .gray5)
+ .padding(.top, 6)
+
+ VStack(spacing: 9) {
+ if entry.todayMission.isEmpty {
+ Button(action: {
+ print("앱으로 이동")
+ }, label: {
+ Image(.icPlusDark)
+ .imageScale(.large)
+ })
+ .buttonStyle(.plain)
+ .position(x: 10, y: 9)
+ } else {
+ ForEach(entry.todayMission) { task in
+ HStack {
+ Button(intent: ToggleButtonIntent(id: task.id, status: task.completionStatus.rawValue)) {
+ Image(task.completionStatus == .CHECKED ? .btnMediumFill : .btnMedium)
+ }
+ .buttonStyle(.plain)
+ .frame(width: 19, height: 19)
+
+ Text(task.title)
+ .foregroundStyle(task.completionStatus == .CHECKED ? .gray4 : .ntdBlack)
+ .strikethrough(task.completionStatus == .CHECKED, color: .gray4)
+ .font(.custom("Pretendard-Regular", size: 11))
+ .fontWeight(.regular)
+ Spacer()
+ }
+ .padding(.leading, 16)
+ .frame(height: 20)
+ }
+ }
+ }
+
+ Spacer()
+ }
+ .padding(.trailing, 22)
+ }
+ .background(.white)
+ }
+}
+
+#Preview(as: .systemMedium) {
+ Widget_NOTTODO()
+} timeline: {
+ SimpleEntry(todayMission: [], quote: "")
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/View/SmallFamily.swift b/iOS-NOTTODO/Widget-NOTTODO/View/SmallFamily.swift
new file mode 100644
index 00000000..bb9eedcd
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/View/SmallFamily.swift
@@ -0,0 +1,79 @@
+//
+// SmallFamily.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 4/19/24.
+//
+
+import SwiftUI
+import WidgetKit
+
+struct SmallFamily: View {
+ var entry: Provider.Entry
+
+ var body: some View {
+ let progressPercent = Double(entry.todayMission.filter { $0.completionStatus == .CHECKED }.count) / Double(entry.todayMission.count)
+
+ VStack {
+ HStack {
+ Spacer()
+
+ ZStack {
+ Text(entry.dayOfWeek)
+ .foregroundStyle(entry.dayOfWeek == "일" ? .wdgRed : .white)
+ .font(.custom("Pretendard", size: 13))
+ .fontWeight(.semibold)
+
+ CircularProgressBarView(percent: progressPercent, size: 27, lineWidth: 3)
+ }
+
+ Text(entry.quote)
+ .foregroundStyle(.white)
+ .font(.custom("Pretendard", size: 7))
+ .fontWeight(.regular)
+ .lineLimit(2)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Spacer()
+ }
+ .frame(maxWidth: .infinity, maxHeight: 48)
+ .background(.ntdBlack)
+
+ VStack(spacing: 12) {
+ if entry.todayMission.isEmpty {
+ Button(action: {
+ print("앱으로 이동")
+ }, label: {
+ Image(.icPlus)
+ .imageScale(.large)
+ })
+ .buttonStyle(.plain)
+ .position(x: 17, y: 12)
+ } else {
+ ForEach(entry.todayMission) { task in
+ HStack {
+ Button(intent: ToggleButtonIntent(id: task.id, status: task.completionStatus.rawValue)) {
+ Image(task.completionStatus == .CHECKED ? .btnSmallBoxFill : .btnSmallBox)
+ }
+ .buttonStyle(.plain)
+ .frame(width: 16, height: 16)
+
+ Text(task.title)
+ .foregroundStyle(task.completionStatus == .CHECKED ? .gray4 : .ntdBlack)
+ .strikethrough(task.completionStatus == .CHECKED, color: .gray4)
+ .font(.custom("Pretendard-Regular", size: 8))
+ .fontWeight(.regular)
+ .lineLimit(2)
+ Spacer()
+ }
+ .padding(.leading, 16)
+ .frame(height: 22)
+ }
+ }
+ }
+
+ Spacer()
+ }
+ .background(.bg)
+ }
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtension.entitlements b/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtension.entitlements
new file mode 100644
index 00000000..71b14c10
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtension.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.nottodo.iOS-NOTTODO
+
+
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtensionRelease.entitlements b/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtensionRelease.entitlements
new file mode 100644
index 00000000..71b14c10
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Widget-NOTTODOExtensionRelease.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.nottodo.iOS-NOTTODO
+
+
+
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODO.swift b/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODO.swift
new file mode 100644
index 00000000..c7121493
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODO.swift
@@ -0,0 +1,45 @@
+//
+// Widget_NOTTODO.swift
+// Widget-NOTTODO
+//
+// Created by 강윤서 on 4/14/24.
+//
+
+import WidgetKit
+import SwiftUI
+
+struct Widget_NOTTODOEntryView: View {
+ @Environment(\.widgetFamily) var family: WidgetFamily
+ var entry: Provider.Entry
+
+ @ViewBuilder
+ var body: some View {
+ switch self.family {
+ case .systemSmall:
+ SmallFamily(entry: entry)
+ default:
+ MediumFamily(entry: entry)
+ }
+ }
+}
+
+struct Widget_NOTTODO: Widget {
+ let kind: String = "Widget_NOTTODO"
+
+ var body: some WidgetConfiguration {
+ AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in
+ Widget_NOTTODOEntryView(entry: entry)
+ .containerBackground(.fill.tertiary, for: .widget)
+ }
+ .configurationDisplayName("오늘의 낫투두")
+ .description("오늘 실천할 낫투두를 확인하고 명언을 통해 동기부여를 얻을 수 있어요")
+ .contentMarginsDisabled()
+ .supportedFamilies([.systemSmall, .systemMedium])
+ }
+}
+
+#Preview(as: .systemMedium) {
+ Widget_NOTTODO()
+} timeline: {
+ SimpleEntry(todayMission: [], quote: "")
+}
diff --git a/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODOBundle.swift b/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODOBundle.swift
new file mode 100644
index 00000000..42b803c1
--- /dev/null
+++ b/iOS-NOTTODO/Widget-NOTTODO/Widget_NOTTODOBundle.swift
@@ -0,0 +1,16 @@
+//
+// Widget_NOTTODOBundle.swift
+// Widget-NOTTODO
+//
+// Created by 강윤서 on 4/14/24.
+//
+
+import WidgetKit
+import SwiftUI
+
+@main
+struct Widget_NOTTODOBundle: WidgetBundle {
+ var body: some Widget {
+ Widget_NOTTODO()
+ }
+}
diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj
index 98448be5..4535096b 100644
--- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj
+++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj
@@ -79,7 +79,6 @@
09F6719029CB6AB400708725 /* OnboardingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6718F29CB6AB400708725 /* OnboardingFooterView.swift */; };
09F6719529CBFCD200708725 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6719429CBFCD200708725 /* GradientView.swift */; };
09F6719729CC81B500708725 /* DetailAchievementCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6719629CC81B500708725 /* DetailAchievementCollectionViewCell.swift */; };
- 155E45662B5FF089008628E7 /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */; };
155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155E45682B5FF2EE008628E7 /* FirebaseUtil.swift */; };
155E456D2B62B1A1008628E7 /* UpdateCheckViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155E456C2B62B1A1008628E7 /* UpdateCheckViewController.swift */; };
3B027A78299C31B500BEB65C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027A77299C31B500BEB65C /* AppDelegate.swift */; };
@@ -113,7 +112,19 @@
3B14A13F29A6FCB300F92897 /* UIStackView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B14A13E29A6FCB300F92897 /* UIStackView+.swift */; };
3B14A14129A6FDA900F92897 /* UILabel+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B14A14029A6FDA900F92897 /* UILabel+.swift */; };
3B14A14329A6FEE400F92897 /* UITextField+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B14A14229A6FEE400F92897 /* UITextField+.swift */; };
- 3B2B59442AEB814B00B4619A /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 3B2B59432AEB814B00B4619A /* FirebaseMessaging */; };
+ 3B2AA7E52BD62D1200725457 /* URLConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705A29A68EA9008D145C /* URLConstant.swift */; };
+ 3B3105D22BCBF70600964025 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B1E84932BAC897C002F9808 /* WidgetKit.framework */; };
+ 3B3105D32BCBF70600964025 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B1E84952BAC897C002F9808 /* SwiftUI.framework */; };
+ 3B3105D62BCBF70600964025 /* Widget_NOTTODOBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3105D52BCBF70600964025 /* Widget_NOTTODOBundle.swift */; };
+ 3B3105D82BCBF70600964025 /* Widget_NOTTODO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3105D72BCBF70600964025 /* Widget_NOTTODO.swift */; };
+ 3B3105DA2BCBF70600964025 /* AppIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3105D92BCBF70600964025 /* AppIntent.swift */; };
+ 3B3105DC2BCBF70700964025 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3B3105DB2BCBF70700964025 /* Assets.xcassets */; };
+ 3B3105E02BCBF70700964025 /* Widget-NOTTODOExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3B3105D12BCBF70600964025 /* Widget-NOTTODOExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 3B3105E92BCC00D800964025 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3B027AAB299C35E500BEB65C /* Colors.xcassets */; };
+ 3B3105EB2BCC022500964025 /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027A93299C340600BEB65C /* UIFont+.swift */; };
+ 3B35F57C2BF091A60050D450 /* NetworkConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706029A69096008D145C /* NetworkConstant.swift */; };
+ 3B35F57D2BF093120050D450 /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; };
+ 3B35F5822BF0A4770050D450 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B35F5802BF0A36F0050D450 /* Formatter.swift */; };
3B37AE2929C8821600AB7587 /* GoalCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B37AE2829C8821600AB7587 /* GoalCollectionViewCell.swift */; };
3B37AE2B29C8904800AB7587 /* RecommendKeywordCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B37AE2A29C8904800AB7587 /* RecommendKeywordCollectionViewCell.swift */; };
3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */; };
@@ -132,18 +143,43 @@
3B5F8F8129BF90190063A7F8 /* NottodoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */; };
3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F8229BF90290063A7F8 /* SituationCollectionViewCell.swift */; };
3B5F8F8929BF9EFE0063A7F8 /* AddMissionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F8829BF9EFE0063A7F8 /* AddMissionLabel.swift */; };
+ 3B706A6C2BD5746A00CD6C74 /* UserDefaults+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B706A6B2BD5746A00CD6C74 /* UserDefaults+.swift */; };
+ 3B706A6D2BD5771100CD6C74 /* UserDefaults+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B706A6B2BD5746A00CD6C74 /* UserDefaults+.swift */; };
3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3B710A5B2A62D4AB00E95620 /* Settings.bundle */; };
3B80B5D52B7F304D00697250 /* adjust+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B80B5D42B7F304D00697250 /* adjust+.swift */; };
3B80B5D72B7F30E200697250 /* Numbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B80B5D62B7F30E200697250 /* Numbers.swift */; };
3B892ABB2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B892ABA2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift */; };
3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B9532F32A284CC1006510F8 /* ModalProtocol.swift */; };
+ 3B99131C2BF08B5B00FA9328 /* DefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */; };
+ 3B999D0C2BAEC24100E562EE /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 3B999D0B2BAEC24100E562EE /* FirebaseRemoteConfig */; };
+ 3B999D0E2BAEC24800E562EE /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 3B999D0D2BAEC24800E562EE /* FirebaseMessaging */; };
+ 3BAFA50F2BCFBF1F007569DD /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B4E12F92A27C4DD001D1EC1 /* Pretendard-Bold.otf */; };
+ 3BAFA5102BCFBF22007569DD /* Pretendard-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B146D9D299D081400B17B62 /* Pretendard-SemiBold.otf */; };
+ 3BAFA5112BCFBF24007569DD /* Pretendard-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B146D9B299D07D500B17B62 /* Pretendard-Regular.otf */; };
+ 3BAFA5122BCFBF26007569DD /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B146D99299D079D00B17B62 /* Pretendard-Light.otf */; };
+ 3BAFA5132BCFBF29007569DD /* Pretendard-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B146D97299D077800B17B62 /* Pretendard-Medium.otf */; };
+ 3BAFA51A2BD01C2C007569DD /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAFA5182BD01994007569DD /* CircularProgressBar.swift */; };
+ 3BB5912D2BD3B103003FB77C /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B570B332BA30E6100418250 /* API_KEY.plist */; };
+ 3BB5912E2BD3B212003FB77C /* Bundle+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0943A9F82A53239200614761 /* Bundle+.swift */; };
+ 3BB591302BD3B343003FB77C /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; };
+ 3BB5CFE52BD19639006326B5 /* SmallFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB5CFE32BD19609006326B5 /* SmallFamily.swift */; };
+ 3BB5CFE82BD1978E006326B5 /* MediumFamily.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB5CFE62BD1978C006326B5 /* MediumFamily.swift */; };
+ 3BB5CFEC2BD1A04D006326B5 /* HorizontalDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB5CFEB2BD1A04D006326B5 /* HorizontalDivider.swift */; };
3BBB6C8F2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BBB6C8E2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift */; };
3BC19A9329CA1CA800C02803 /* UICollectionViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC19A9229CA1CA800C02803 /* UICollectionViewCell+.swift */; };
3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1A27129C9AF310088376B /* MissionHistoryModels.swift */; };
3BC1A27429C9AF500088376B /* MissionHistoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1A27329C9AF500088376B /* MissionHistoryCollectionViewCell.swift */; };
3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1A27829C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift */; };
3BD3B5C829B8F82C00D3575B /* AddMissionTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BD3B5C729B8F82C00D3575B /* AddMissionTextFieldView.swift */; };
+ 3BEC83AA2BEDF65000632FA0 /* GeneralResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705E29A69025008D145C /* GeneralResponse.swift */; };
+ 3BEC83AE2BEE06B900632FA0 /* QuoteResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BEC83AC2BEDF73A00632FA0 /* QuoteResponseDTO.swift */; };
+ 3BEC83AF2BEE06BD00632FA0 /* WidgetService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BEC83A52BEDF3DE00632FA0 /* WidgetService.swift */; };
3BEEBE972A4B048A0081C936 /* NottodoToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BEEBE962A4B048A0081C936 /* NottodoToastView.swift */; };
+ 3BF8C68F2BEE6FCE0003D8FE /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706229A690CD008D145C /* NetworkResult.swift */; };
+ 3BF8C6912BEE70CF0003D8FE /* NetworkBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706429A690E5008D145C /* NetworkBase.swift */; };
+ 3BF8C6952BEE74A40003D8FE /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF8C6932BEE746D0003D8FE /* NetworkError.swift */; };
+ 3BF8C6992BEE79830003D8FE /* TimeEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF8C6972BEE795E0003D8FE /* TimeEntity.swift */; };
+ 3BF8C69C2BEE7ACF0003D8FE /* MissionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF8C69A2BEE7ABB0003D8FE /* MissionProvider.swift */; };
6C049A312A595C670085E40B /* logo.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 6C049A302A595C670085E40B /* logo.mp4 */; };
6C16015829C40112005AE3F5 /* AuthButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C16015729C40112005AE3F5 /* AuthButtonView.swift */; };
6C16015C29C56DBA005AE3F5 /* MyInfoAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C16015B29C56DBA005AE3F5 /* MyInfoAccountViewController.swift */; };
@@ -187,6 +223,16 @@
6CF4707A29A7AAFF008D145C /* PaddingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4707929A7AAFF008D145C /* PaddingLabel.swift */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 3B3105DE2BCBF70700964025 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 3B027A6C299C31B500BEB65C /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 3B3105D02BCBF70600964025;
+ remoteInfo = "Widget-NOTTODOExtension";
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
3B1E84A92BAC897E002F9808 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
@@ -194,6 +240,7 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
+ 3B3105E02BCBF70700964025 /* Widget-NOTTODOExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
@@ -305,6 +352,13 @@
3B14A14229A6FEE400F92897 /* UITextField+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+.swift"; sourceTree = ""; };
3B1E84932BAC897C002F9808 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
3B1E84952BAC897C002F9808 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
+ 3B3105D12BCBF70600964025 /* Widget-NOTTODOExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Widget-NOTTODOExtension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B3105D52BCBF70600964025 /* Widget_NOTTODOBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widget_NOTTODOBundle.swift; sourceTree = ""; };
+ 3B3105D72BCBF70600964025 /* Widget_NOTTODO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widget_NOTTODO.swift; sourceTree = ""; };
+ 3B3105D92BCBF70600964025 /* AppIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntent.swift; sourceTree = ""; };
+ 3B3105DB2BCBF70700964025 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 3B3105DD2BCBF70700964025 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 3B35F5802BF0A36F0050D450 /* Formatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; };
3B37AE2829C8821600AB7587 /* GoalCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalCollectionViewCell.swift; sourceTree = ""; };
3B37AE2A29C8904800AB7587 /* RecommendKeywordCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendKeywordCollectionViewCell.swift; sourceTree = ""; };
3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
@@ -323,11 +377,19 @@
3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NottodoCollectionViewCell.swift; sourceTree = ""; };
3B5F8F8229BF90290063A7F8 /* SituationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SituationCollectionViewCell.swift; sourceTree = ""; };
3B5F8F8829BF9EFE0063A7F8 /* AddMissionLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionLabel.swift; sourceTree = ""; };
+ 3B706A6B2BD5746A00CD6C74 /* UserDefaults+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+.swift"; sourceTree = ""; };
3B710A5B2A62D4AB00E95620 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; };
3B80B5D42B7F304D00697250 /* adjust+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "adjust+.swift"; sourceTree = ""; };
3B80B5D62B7F30E200697250 /* Numbers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Numbers.swift; sourceTree = ""; };
+ 3B857AD22BD589A300CEB1D5 /* iOS-NOTTODORelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "iOS-NOTTODORelease.entitlements"; sourceTree = ""; };
+ 3B857AD32BD589B700CEB1D5 /* Widget-NOTTODOExtensionRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Widget-NOTTODOExtensionRelease.entitlements"; sourceTree = ""; };
3B892ABA2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendSituationResponseDTO.swift; sourceTree = ""; };
3B9532F32A284CC1006510F8 /* ModalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalProtocol.swift; sourceTree = ""; };
+ 3BAFA5182BD01994007569DD /* CircularProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = ""; };
+ 3BB4A51D2BD246A600900C86 /* Widget-NOTTODOExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Widget-NOTTODOExtension.entitlements"; sourceTree = ""; };
+ 3BB5CFE32BD19609006326B5 /* SmallFamily.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallFamily.swift; sourceTree = ""; };
+ 3BB5CFE62BD1978C006326B5 /* MediumFamily.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediumFamily.swift; sourceTree = ""; };
+ 3BB5CFEB2BD1A04D006326B5 /* HorizontalDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalDivider.swift; sourceTree = ""; };
3BBB6C8E2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewLeftAlignLayout.swift; sourceTree = ""; };
3BC19A9229CA1CA800C02803 /* UICollectionViewCell+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+.swift"; sourceTree = ""; };
3BC1A27129C9AF310088376B /* MissionHistoryModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MissionHistoryModels.swift; sourceTree = ""; };
@@ -335,7 +397,12 @@
3BC1A27829C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionFooterCollectionReusableView.swift; sourceTree = ""; };
3BD3B5C729B8F82C00D3575B /* AddMissionTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionTextFieldView.swift; sourceTree = ""; };
3BDE6157299EDD02001CCEA9 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; };
+ 3BEC83A52BEDF3DE00632FA0 /* WidgetService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetService.swift; sourceTree = ""; };
+ 3BEC83AC2BEDF73A00632FA0 /* QuoteResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteResponseDTO.swift; sourceTree = ""; };
3BEEBE962A4B048A0081C936 /* NottodoToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NottodoToastView.swift; sourceTree = ""; };
+ 3BF8C6932BEE746D0003D8FE /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; };
+ 3BF8C6972BEE795E0003D8FE /* TimeEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeEntity.swift; sourceTree = ""; };
+ 3BF8C69A2BEE7ABB0003D8FE /* MissionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionProvider.swift; sourceTree = ""; };
6C049A302A595C670085E40B /* logo.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = logo.mp4; sourceTree = ""; };
6C16015729C40112005AE3F5 /* AuthButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthButtonView.swift; sourceTree = ""; };
6C16015B29C56DBA005AE3F5 /* MyInfoAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoAccountViewController.swift; sourceTree = ""; };
@@ -382,9 +449,9 @@
6C44127F29A35A1000313C3F /* KakaoSDKTemplate in Frameworks */,
6C44127129A35A1000313C3F /* KakaoSDK in Frameworks */,
3B146DA4299D0A8600B17B62 /* Then in Frameworks */,
+ 3B999D0C2BAEC24100E562EE /* FirebaseRemoteConfig in Frameworks */,
3B146DA7299D0AA300B17B62 /* Moya in Frameworks */,
- 155E45662B5FF089008628E7 /* FirebaseRemoteConfig in Frameworks */,
- 3B2B59442AEB814B00B4619A /* FirebaseMessaging in Frameworks */,
+ 3B999D0E2BAEC24800E562EE /* FirebaseMessaging in Frameworks */,
6C44127729A35A1000313C3F /* KakaoSDKNavi in Frameworks */,
6C44128129A35A1000313C3F /* KakaoSDKUser in Frameworks */,
6C44127D29A35A1000313C3F /* KakaoSDKTalk in Frameworks */,
@@ -395,6 +462,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3B3105CE2BCBF70600964025 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3B3105D32BCBF70600964025 /* SwiftUI.framework in Frameworks */,
+ 3B3105D22BCBF70600964025 /* WidgetKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -728,6 +804,7 @@
3B710A5B2A62D4AB00E95620 /* Settings.bundle */,
3BDE6157299EDD02001CCEA9 /* .swiftlint.yml */,
3B027A76299C31B500BEB65C /* iOS-NOTTODO */,
+ 3B3105D42BCBF70600964025 /* Widget-NOTTODO */,
3B027A75299C31B500BEB65C /* Products */,
155E45642B5FF089008628E7 /* Frameworks */,
);
@@ -737,6 +814,7 @@
isa = PBXGroup;
children = (
3B027A74299C31B500BEB65C /* iOS-NOTTODO.app */,
+ 3B3105D12BCBF70600964025 /* Widget-NOTTODOExtension.appex */,
);
name = Products;
sourceTree = "";
@@ -744,6 +822,7 @@
3B027A76299C31B500BEB65C /* iOS-NOTTODO */ = {
isa = PBXGroup;
children = (
+ 3B857AD22BD589A300CEB1D5 /* iOS-NOTTODORelease.entitlements */,
6CF4705829A60A79008D145C /* iOS-NOTTODO.entitlements */,
0989042D2B81BB22004AAD3C /* Coordinator */,
3B027A8B299C335E00BEB65C /* Application */,
@@ -795,6 +874,7 @@
0943A9F82A53239200614761 /* Bundle+.swift */,
3B03D0D72B0F5EF300302872 /* CGSize+.swift */,
3B80B5D42B7F304D00697250 /* adjust+.swift */,
+ 3B706A6B2BD5746A00CD6C74 /* UserDefaults+.swift */,
);
path = Extensions;
sourceTree = "";
@@ -965,6 +1045,56 @@
path = ViewControllers;
sourceTree = "";
};
+ 3B3105D42BCBF70600964025 /* Widget-NOTTODO */ = {
+ isa = PBXGroup;
+ children = (
+ 3B35F57E2BF0A34D0050D450 /* Global */,
+ 3BF8C6962BEE79510003D8FE /* Provider */,
+ 3BEC83A32BEDF3BF00632FA0 /* Network */,
+ 3BB4A51D2BD246A600900C86 /* Widget-NOTTODOExtension.entitlements */,
+ 3B857AD32BD589B700CEB1D5 /* Widget-NOTTODOExtensionRelease.entitlements */,
+ 3BB5CFE22BD195FA006326B5 /* View */,
+ 3BAFA5142BCFC5F4007569DD /* Resource */,
+ 3B3105D52BCBF70600964025 /* Widget_NOTTODOBundle.swift */,
+ 3B3105D72BCBF70600964025 /* Widget_NOTTODO.swift */,
+ 3B3105D92BCBF70600964025 /* AppIntent.swift */,
+ );
+ path = "Widget-NOTTODO";
+ sourceTree = "";
+ };
+ 3B3105EE2BCC16FA00964025 /* Common */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3105EF2BCC173200964025 /* Components */,
+ );
+ path = Common;
+ sourceTree = "";
+ };
+ 3B3105EF2BCC173200964025 /* Components */ = {
+ isa = PBXGroup;
+ children = (
+ 3BAFA5182BD01994007569DD /* CircularProgressBar.swift */,
+ 3BB5CFEB2BD1A04D006326B5 /* HorizontalDivider.swift */,
+ );
+ path = Components;
+ sourceTree = "";
+ };
+ 3B35F57E2BF0A34D0050D450 /* Global */ = {
+ isa = PBXGroup;
+ children = (
+ 3B35F57F2BF0A3610050D450 /* Extensions */,
+ );
+ path = Global;
+ sourceTree = "";
+ };
+ 3B35F57F2BF0A3610050D450 /* Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ 3B35F5802BF0A36F0050D450 /* Formatter.swift */,
+ );
+ path = Extensions;
+ sourceTree = "";
+ };
3B3C89DB29C0EF6A00B1D56D /* Models */ = {
isa = PBXGroup;
children = (
@@ -1102,6 +1232,25 @@
path = Protocol;
sourceTree = "";
};
+ 3BAFA5142BCFC5F4007569DD /* Resource */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3105DB2BCBF70700964025 /* Assets.xcassets */,
+ 3B3105DD2BCBF70700964025 /* Info.plist */,
+ );
+ path = Resource;
+ sourceTree = "";
+ };
+ 3BB5CFE22BD195FA006326B5 /* View */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3105EE2BCC16FA00964025 /* Common */,
+ 3BB5CFE32BD19609006326B5 /* SmallFamily.swift */,
+ 3BB5CFE62BD1978C006326B5 /* MediumFamily.swift */,
+ );
+ path = View;
+ sourceTree = "";
+ };
3BD3B5C629B8F80C00D3575B /* AddMissionTextFieldView */ = {
isa = PBXGroup;
children = (
@@ -1110,6 +1259,32 @@
path = AddMissionTextFieldView;
sourceTree = "";
};
+ 3BEC83A32BEDF3BF00632FA0 /* Network */ = {
+ isa = PBXGroup;
+ children = (
+ 3BF8C6922BEE744D0003D8FE /* Base */,
+ 3BEC83AB2BEDF71A00632FA0 /* DataModel */,
+ 3BEC83A42BEDF3D100632FA0 /* Service */,
+ );
+ path = Network;
+ sourceTree = "";
+ };
+ 3BEC83A42BEDF3D100632FA0 /* Service */ = {
+ isa = PBXGroup;
+ children = (
+ 3BEC83A52BEDF3DE00632FA0 /* WidgetService.swift */,
+ );
+ path = Service;
+ sourceTree = "";
+ };
+ 3BEC83AB2BEDF71A00632FA0 /* DataModel */ = {
+ isa = PBXGroup;
+ children = (
+ 3BEC83AC2BEDF73A00632FA0 /* QuoteResponseDTO.swift */,
+ );
+ path = DataModel;
+ sourceTree = "";
+ };
3BEEBE952A4AF6840081C936 /* Toast */ = {
isa = PBXGroup;
children = (
@@ -1118,6 +1293,23 @@
path = Toast;
sourceTree = "";
};
+ 3BF8C6922BEE744D0003D8FE /* Base */ = {
+ isa = PBXGroup;
+ children = (
+ 3BF8C6932BEE746D0003D8FE /* NetworkError.swift */,
+ );
+ path = Base;
+ sourceTree = "";
+ };
+ 3BF8C6962BEE79510003D8FE /* Provider */ = {
+ isa = PBXGroup;
+ children = (
+ 3BF8C6972BEE795E0003D8FE /* TimeEntity.swift */,
+ 3BF8C69A2BEE7ABB0003D8FE /* MissionProvider.swift */,
+ );
+ path = Provider;
+ sourceTree = "";
+ };
6C16015929C56CE8005AE3F5 /* MyInfoAccount */ = {
isa = PBXGroup;
children = (
@@ -1267,6 +1459,7 @@
buildRules = (
);
dependencies = (
+ 3B3105DF2BCBF70700964025 /* PBXTargetDependency */,
);
name = "iOS-NOTTODO";
packageProductDependencies = (
@@ -1285,13 +1478,30 @@
6C9628A62A22208F003ADE25 /* Lottie */,
0943A9F42A531D0000614761 /* Amplitude */,
09C8602C2AB14B4800C4F4B1 /* FSCalendar */,
- 3B2B59432AEB814B00B4619A /* FirebaseMessaging */,
- 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */,
+ 3B999D0B2BAEC24100E562EE /* FirebaseRemoteConfig */,
+ 3B999D0D2BAEC24800E562EE /* FirebaseMessaging */,
);
productName = "iOS-NOTTODO";
productReference = 3B027A74299C31B500BEB65C /* iOS-NOTTODO.app */;
productType = "com.apple.product-type.application";
};
+ 3B3105D02BCBF70600964025 /* Widget-NOTTODOExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3B3105E32BCBF70700964025 /* Build configuration list for PBXNativeTarget "Widget-NOTTODOExtension" */;
+ buildPhases = (
+ 3B3105CD2BCBF70600964025 /* Sources */,
+ 3B3105CE2BCBF70600964025 /* Frameworks */,
+ 3B3105CF2BCBF70600964025 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Widget-NOTTODOExtension";
+ productName = "Widget-NOTTODOExtension";
+ productReference = 3B3105D12BCBF70600964025 /* Widget-NOTTODOExtension.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -1299,12 +1509,15 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1530;
+ LastSwiftUpdateCheck = 1520;
LastUpgradeCheck = 1420;
TargetAttributes = {
3B027A73299C31B500BEB65C = {
CreatedOnToolsVersion = 14.2;
};
+ 3B3105D02BCBF70600964025 = {
+ CreatedOnToolsVersion = 15.2;
+ };
};
};
buildConfigurationList = 3B027A6F299C31B500BEB65C /* Build configuration list for PBXProject "iOS-NOTTODO" */;
@@ -1325,13 +1538,14 @@
6C9628A52A22208F003ADE25 /* XCRemoteSwiftPackageReference "lottie-ios" */,
0943A9F32A531D0000614761 /* XCRemoteSwiftPackageReference "Amplitude-iOS" */,
09C8602B2AB14B4700C4F4B1 /* XCRemoteSwiftPackageReference "FSCalendar" */,
- 3B2B59422AEB814B00B4619A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
+ 3B999D0A2BAEC1F100E562EE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
productRefGroup = 3B027A75299C31B500BEB65C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
3B027A73299C31B500BEB65C /* iOS-NOTTODO */,
+ 3B3105D02BCBF70600964025 /* Widget-NOTTODOExtension */,
);
};
/* End PBXProject section */
@@ -1357,6 +1571,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3B3105CF2BCBF70600964025 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3B3105DC2BCBF70700964025 /* Assets.xcassets in Resources */,
+ 3BB5912D2BD3B103003FB77C /* API_KEY.plist in Resources */,
+ 3BAFA5112BCFBF24007569DD /* Pretendard-Regular.otf in Resources */,
+ 3BAFA50F2BCFBF1F007569DD /* Pretendard-Bold.otf in Resources */,
+ 3BAFA5132BCFBF29007569DD /* Pretendard-Medium.otf in Resources */,
+ 3BAFA5102BCFBF22007569DD /* Pretendard-SemiBold.otf in Resources */,
+ 3BAFA5122BCFBF26007569DD /* Pretendard-Light.otf in Resources */,
+ 3B3105E92BCC00D800964025 /* Colors.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -1483,6 +1712,7 @@
3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */,
6CD4F8BA29AA493600CCC740 /* RecommendActionHeaderView.swift in Sources */,
0930DE6229B80550007958DE /* MissionDetailViewController.swift in Sources */,
+ 3B706A6C2BD5746A00CD6C74 /* UserDefaults+.swift in Sources */,
0989044C2B81C210004AAD3C /* HomecoordinatorImpl.swift in Sources */,
3B14A13F29A6FCB300F92897 /* UIStackView+.swift in Sources */,
6CD4F8BC29AA494300CCC740 /* RecommendActionFooterView.swift in Sources */,
@@ -1537,8 +1767,47 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3B3105CD2BCBF70600964025 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3BB5CFE82BD1978E006326B5 /* MediumFamily.swift in Sources */,
+ 3B3105D62BCBF70600964025 /* Widget_NOTTODOBundle.swift in Sources */,
+ 3BB591302BD3B343003FB77C /* DailyMissionResponseDTO.swift in Sources */,
+ 3B706A6D2BD5771100CD6C74 /* UserDefaults+.swift in Sources */,
+ 3B2AA7E52BD62D1200725457 /* URLConstant.swift in Sources */,
+ 3B3105EB2BCC022500964025 /* UIFont+.swift in Sources */,
+ 3B35F5822BF0A4770050D450 /* Formatter.swift in Sources */,
+ 3B3105D82BCBF70600964025 /* Widget_NOTTODO.swift in Sources */,
+ 3BF8C68F2BEE6FCE0003D8FE /* NetworkResult.swift in Sources */,
+ 3B99131C2BF08B5B00FA9328 /* DefaultKeys.swift in Sources */,
+ 3BF8C6912BEE70CF0003D8FE /* NetworkBase.swift in Sources */,
+ 3BB5CFEC2BD1A04D006326B5 /* HorizontalDivider.swift in Sources */,
+ 3BB5912E2BD3B212003FB77C /* Bundle+.swift in Sources */,
+ 3BAFA51A2BD01C2C007569DD /* CircularProgressBar.swift in Sources */,
+ 3BEC83AA2BEDF65000632FA0 /* GeneralResponse.swift in Sources */,
+ 3B3105DA2BCBF70600964025 /* AppIntent.swift in Sources */,
+ 3BF8C6992BEE79830003D8FE /* TimeEntity.swift in Sources */,
+ 3BEC83AF2BEE06BD00632FA0 /* WidgetService.swift in Sources */,
+ 3BF8C6952BEE74A40003D8FE /* NetworkError.swift in Sources */,
+ 3B35F57C2BF091A60050D450 /* NetworkConstant.swift in Sources */,
+ 3B35F57D2BF093120050D450 /* KeychainUtil.swift in Sources */,
+ 3BF8C69C2BEE7ACF0003D8FE /* MissionProvider.swift in Sources */,
+ 3BB5CFE52BD19639006326B5 /* SmallFamily.swift in Sources */,
+ 3BEC83AE2BEE06B900632FA0 /* QuoteResponseDTO.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 3B3105DF2BCBF70700964025 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 3B3105D02BCBF70600964025 /* Widget-NOTTODOExtension */;
+ targetProxy = 3B3105DE2BCBF70700964025 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
3B027A82299C31B600BEB65C /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
@@ -1668,6 +1937,7 @@
3B027A89299C31B600BEB65C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "iOS-NOTTODO/iOS-NOTTODO.entitlements";
@@ -1689,7 +1959,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.4;
+ MARKETING_VERSION = 1.0.5;
PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1706,9 +1976,10 @@
3B027A8A299C31B600BEB65C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
- CODE_SIGN_ENTITLEMENTS = "iOS-NOTTODO/iOS-NOTTODO.entitlements";
+ CODE_SIGN_ENTITLEMENTS = "iOS-NOTTODO/iOS-NOTTODORelease.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
@@ -1727,7 +1998,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0.4;
+ MARKETING_VERSION = 1.0.5;
PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1741,6 +2012,83 @@
};
name = Release;
};
+ 3B3105E12BCBF70700964025 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = "Widget-NOTTODO/Widget-NOTTODOExtension.entitlements";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CQJ9UKUU35;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Widget-NOTTODO/Resource/Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = "Widget-NOTTODO";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO.Widget-NOTTODO";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = nottodoWidgetDebug;
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 3B3105E22BCBF70700964025 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = "Widget-NOTTODO/Widget-NOTTODOExtensionRelease.entitlements";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CQJ9UKUU35;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Widget-NOTTODO/Resource/Info.plist";
+ INFOPLIST_KEY_CFBundleDisplayName = "Widget-NOTTODO";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 17.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO.Widget-NOTTODO";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = nottodoWidgetRelease;
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -1762,6 +2110,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 3B3105E32BCBF70700964025 /* Build configuration list for PBXNativeTarget "Widget-NOTTODOExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3B3105E12BCBF70700964025 /* Debug */,
+ 3B3105E22BCBF70700964025 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@@ -1805,20 +2162,21 @@
kind = branch;
};
};
- 3B2B59422AEB814B00B4619A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
+ 3B999D0A2BAEC1F100E562EE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
requirement = {
kind = upToNextMajorVersion;
- minimumVersion = 10.0.0;
+ minimumVersion = 10.23.0;
};
};
6C44126F29A35A1000313C3F /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kakao/kakao-ios-sdk";
requirement = {
- branch = master;
- kind = branch;
+ kind = versionRange;
+ maximumVersion = 2.21.1;
+ minimumVersion = 2.21.1;
};
};
6C9628A52A22208F003ADE25 /* XCRemoteSwiftPackageReference "lottie-ios" */ = {
@@ -1850,11 +2208,6 @@
package = 09C8602B2AB14B4700C4F4B1 /* XCRemoteSwiftPackageReference "FSCalendar" */;
productName = FSCalendar;
};
- 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */ = {
- isa = XCSwiftPackageProductDependency;
- package = 3B2B59422AEB814B00B4619A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
- productName = FirebaseRemoteConfig;
- };
3B146DA0299D0A7A00B17B62 /* SnapKit */ = {
isa = XCSwiftPackageProductDependency;
package = 3B146D9F299D0A7A00B17B62 /* XCRemoteSwiftPackageReference "SnapKit" */;
@@ -1870,9 +2223,14 @@
package = 3B146DA5299D0AA300B17B62 /* XCRemoteSwiftPackageReference "Moya" */;
productName = Moya;
};
- 3B2B59432AEB814B00B4619A /* FirebaseMessaging */ = {
+ 3B999D0B2BAEC24100E562EE /* FirebaseRemoteConfig */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 3B999D0A2BAEC1F100E562EE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseRemoteConfig;
+ };
+ 3B999D0D2BAEC24800E562EE /* FirebaseMessaging */ = {
isa = XCSwiftPackageProductDependency;
- package = 3B2B59422AEB814B00B4619A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ package = 3B999D0A2BAEC1F100E562EE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseMessaging;
};
6C44127029A35A1000313C3F /* KakaoSDK */ = {
diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..0c67376e
--- /dev/null
+++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/xcshareddata/xcschemes/Widget-NOTTODOExtension.xcscheme b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/xcshareddata/xcschemes/Widget-NOTTODOExtension.xcscheme
new file mode 100644
index 00000000..353d816a
--- /dev/null
+++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/xcshareddata/xcschemes/Widget-NOTTODOExtension.xcscheme
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift
index 82c40c0d..0633a7aa 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift
@@ -14,6 +14,7 @@ public final class KeychainUtil {
}
static func setAccessToken(_ token: String) {
UserDefaults.standard.setValue(token, forKey: DefaultKeys.accessToken)
+ UserDefaults.shared?.setValue(token, forKey: DefaultKeys.accessToken)
}
static func setFcmToken(_ token: String) {
UserDefaults.standard.setValue(token, forKey: DefaultKeys.fcmToken)
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift
index d752fe1d..b949a454 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift
@@ -56,4 +56,17 @@ extension Bundle {
}
return value
}
+
+ var appGroups: String {
+ guard let filePath = Bundle.main.path(forResource: "API_KEY", ofType: "plist") else {
+ fatalError("Could't find file 'API_KEY.plist'.")
+ }
+
+ let plist = NSDictionary(contentsOfFile: filePath)
+
+ guard let value = plist?.object(forKey: "APP_GROUPS") as? String else {
+ fatalError("Couldn't find key 'APP_GROUPS' in 'API_KEY.plist'.")
+ }
+ return value
+ }
}
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift
index b2a316ea..d7c3b96d 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Design/UIColor+.swift
@@ -9,9 +9,9 @@ import UIKit
extension UIColor {
static let bg = UIColor(named: "bg")
- static let ntdBlack = UIColor(named: "black")
- static let ntdBlue = UIColor(named: "blue")
- static let ntdRed = UIColor(named: "red")
+ static let ntdBlack = UIColor(named: "ntdBlack")
+ static let ntdBlue = UIColor(named: "ntdBlue")
+ static let ntdRed = UIColor(named: "ntdRed")
static let gray1 = UIColor(named: "gray1")
static let gray2 = UIColor(named: "gray2")
static let gray3 = UIColor(named: "gray3")
@@ -23,7 +23,7 @@ extension UIColor {
static let green1 = UIColor(named: "green1")
static let green2 = UIColor(named: "green2")
static let systemBlack = UIColor(named: "systemBlack")
- static let kakaoYellow = UIColor(named: "yellow")
+ static let kakaoYellow = UIColor(named: "ntdYellow")
static let notiBlack = UIColor(named: "notiBlack")
static let notiGreen = UIColor(named: "notiGreen")
static let notiBlue = UIColor(named: "notiBlue")
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UserDefaults+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UserDefaults+.swift
new file mode 100644
index 00000000..8034788f
--- /dev/null
+++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UserDefaults+.swift
@@ -0,0 +1,31 @@
+//
+// UserDefaults+.swift
+// iOS-NOTTODO
+//
+// Created by 강윤서 on 4/22/24.
+//
+
+import Foundation
+import WidgetKit
+
+extension UserDefaults {
+ static var shared: UserDefaults? {
+ return UserDefaults(suiteName: Bundle.main.appGroups)
+ }
+
+ func setSharedCustomArray(_ value: [T], forKey key: String) {
+ if let encoded = try? JSONEncoder().encode(value) {
+ self.set(encoded, forKey: key)
+ }
+ WidgetCenter.shared.reloadAllTimelines()
+ }
+
+ func getSharedCustomArray(forKey key: String) -> [T]? {
+ if let savedData = self.object(forKey: key) as? Data {
+ if let savedObject = try? JSONDecoder().decode([T].self, from: savedData) {
+ return savedObject
+ }
+ }
+ return nil
+ }
+}
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/URLConstant.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/URLConstant.swift
index a48b56ee..e59dea9c 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/URLConstant.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/URLConstant.swift
@@ -34,4 +34,8 @@ struct URLConstant {
// MARK: - AddMission
static let recentMission = "/recent"
+
+ // MARK: - Widget
+
+ static let quote = "/quote/random"
}
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift
index 01d2cb30..601ea34e 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift
@@ -13,16 +13,17 @@ enum CompletionStatus: String, Codable, Hashable {
// MARK: - DailyMissionResponseDTO
-struct DailyMissionResponseDTO: Codable, Hashable {
+struct DailyMissionResponseDTO: Codable, Hashable, Identifiable {
var uuid = UUID()
let id: Int
let title: String
let situationName: String
let completionStatus: CompletionStatus
+ let date: String
enum CodingKeys: String, CodingKey {
- case id, title, situationName, completionStatus
+ case id, title, situationName, completionStatus, date
}
static func == (lhs: DailyMissionResponseDTO, rhs: DailyMissionResponseDTO) -> Bool {
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift
index 239fa320..05a6a339 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift
@@ -60,10 +60,9 @@ public enum HeaderType {
public var value: [String: String] {
switch self {
case .json:
- return ["Content-Type": "application/json"]
+ return NetworkConstant.noTokenHeader
case .jsonWithToken:
- return ["Content-Type": "application/json",
- "Authorization": "\(KeychainUtil.getAccessToken())"]
+ return NetworkConstant.hasTokenHeader
}
}
}
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift
index c98b7c92..d12d240a 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Recommend/RecommendService.swift
@@ -46,7 +46,7 @@ final class RecommendService: RecommendServiceType {
}
}
- func getRecommendAction(index: Int, completion: @escaping (GeneralResponse?) -> Void) {
+ func getRecommendAction(index: Int, completion: @escaping (ActionData?) -> Void) {
provider.request(.action(id: index)) { result in
switch result {
case .success(let response):
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift
index 9f7e40f6..07207c14 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift
@@ -130,6 +130,12 @@ final class HomeDataSource {
currentSection = newSections
dataSource?.apply(snapshot)
+
+ if let firstMissionDate = missionList.first?.date, !missionList.isEmpty {
+ if firstMissionDate == Utils.dateFormatterString(format: nil, date: Date()) {
+ UserDefaults.shared?.setSharedCustomArray(missionList, forKey: "dailyMission")
+ }
+ }
}
private func createLayout() -> UICollectionViewLayout {
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift
index 60fa3a53..357982f3 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift
+++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift
@@ -245,6 +245,10 @@ extension HomeViewController {
self.missionList = data
self.missionDataSource.updateSnapShot(missionList: data)
+
+ if Utils.dateFormatterString(format: nil, date: today) == date && missionList.isEmpty {
+ UserDefaults.shared?.setSharedCustomArray(missionList, forKey: "dailyMission")
+ }
}
}
@@ -317,6 +321,13 @@ extension HomeViewController {
return sundayInWeek
}
+ private func getDayOfWeek(date: Date) -> String {
+ let calendar = Calendar.current
+ let weekday = calendar.component(.weekday, from: date)
+
+ return I18N.weekDay[weekday-1]
+ }
+
private func getPercentage(for date: Date) -> Float? {
let dateString = Utils.dateFormatterString(format: nil, date: date)
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/black.colorset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdBlack.colorset/Contents.json
similarity index 100%
rename from iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/black.colorset/Contents.json
rename to iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdBlack.colorset/Contents.json
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/blue.colorset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdBlue.colorset/Contents.json
similarity index 100%
rename from iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/blue.colorset/Contents.json
rename to iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdBlue.colorset/Contents.json
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/yellow.colorset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdYellow.colorset/Contents.json
similarity index 100%
rename from iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/yellow.colorset/Contents.json
rename to iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdYellow.colorset/Contents.json
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdred.colorset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdred.colorset/Contents.json
new file mode 100644
index 00000000..84a82a56
--- /dev/null
+++ b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/ntdred.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.392",
+ "green" : "0.392",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.412",
+ "green" : "0.435",
+ "red" : "0.925"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/red.colorset/Contents.json b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/wdgRed.colorset/Contents.json
similarity index 83%
rename from iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/red.colorset/Contents.json
rename to iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/wdgRed.colorset/Contents.json
index 41117905..ca2afcfe 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/red.colorset/Contents.json
+++ b/iOS-NOTTODO/iOS-NOTTODO/Resource/Colors/Colors.xcassets/wdgRed.colorset/Contents.json
@@ -5,8 +5,8 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.392",
- "green" : "0.392",
+ "blue" : "0.271",
+ "green" : "0.271",
"red" : "1.000"
}
},
@@ -23,8 +23,8 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.392",
- "green" : "0.392",
+ "blue" : "0.271",
+ "green" : "0.271",
"red" : "1.000"
}
},
diff --git a/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODO.entitlements b/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODO.entitlements
index 80b5221d..10d5d8bb 100644
--- a/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODO.entitlements
+++ b/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODO.entitlements
@@ -8,5 +8,9 @@
Default
+ com.apple.security.application-groups
+
+ group.nottodo.iOS-NOTTODO
+
diff --git a/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODORelease.entitlements b/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODORelease.entitlements
new file mode 100644
index 00000000..10d5d8bb
--- /dev/null
+++ b/iOS-NOTTODO/iOS-NOTTODO/iOS-NOTTODORelease.entitlements
@@ -0,0 +1,16 @@
+
+
+
+
+ aps-environment
+ development
+ com.apple.developer.applesignin
+
+ Default
+
+ com.apple.security.application-groups
+
+ group.nottodo.iOS-NOTTODO
+
+
+