diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..ee9aec4d Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index e4615943..08b1a429 100644 --- a/.gitignore +++ b/.gitignore @@ -1,166 +1,72 @@ -######################### -# .gitignore file for Xcode4 and Xcode5 Source projects -# -# Apple bugs, waiting for Apple to fix/respond: -# -# 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation? -# -# Version 2.1 -# For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects -# -# 2013 updates: -# - fixed the broken "save personal Schemes" -# - added line-by-line explanations for EVERYTHING (some were missing) -# -# NB: if you are storing "built" products, this WILL NOT WORK, -# and you should use a different .gitignore (or none at all) -# This file is for SOURCE projects, where there are many extra -# files that we want to exclude -# -######################### - -##### -# OS X temporary files that should never be committed -# -# c.f. http://www.westwind.com/reference/os-x/invisibles.html - -.DS_Store - -# c.f. http://www.westwind.com/reference/os-x/invisibles.html - -.Trashes - -# c.f. http://www.westwind.com/reference/os-x/invisibles.html - -*.swp - -# *.lock - this is used and abused by many editors for many different things. -# For the main ones I use (e.g. Eclipse), it should be excluded -# from source-control, but YMMV - -*.lock - -# -# profile - REMOVED temporarily (on double-checking, this seems incorrect; I can't find it in OS X docs?) -#profile - - -#### -# Xcode temporary files that should never be committed -# -# NB: NIB/XIB files still exist even on Storyboard projects, so we want this... - -*~.nib - - -#### -# Xcode build files - -# -# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" - -DerivedData/ - -# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" - +## Build generated build/ +DerivedData/ - -##### -# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) -# -# This is complicated: -# -# SOMETIMES you need to put this file in version control. -# Apple designed it poorly - if you use "custom executables", they are -# saved in this file. -# 99% of projects do NOT use those, so they do NOT want to version control this file. -# ..but if you're in the 1%, comment out the line "*.pbxuser" - -# .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html - +## Various settings *.pbxuser - -# .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html - -*.mode1v3 - -# .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html - -*.mode2v3 - -# .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file - -*.perspectivev3 - -# NB: also, whitelist the default ones, some projects need to use these !default.pbxuser +*.mode1v3 !default.mode1v3 +*.mode2v3 !default.mode2v3 +*.perspectivev3 !default.perspectivev3 +xcuserdata/ - -#### -# Xcode 4 - semi-personal settings -# -# -# OPTION 1: --------------------------------- -# throw away ALL personal settings (including custom schemes! -# - unless they are "shared") -# -# NB: this is exclusive with OPTION 2 below -xcuserdata - -# OPTION 2: --------------------------------- -# get rid of ALL personal settings, but KEEP SOME OF THEM -# - NB: you must manually uncomment the bits you want to keep -# -# NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X, -# or manually install git over the top of the OS X version -# NB: this is exclusive with OPTION 1 above -# -#xcuserdata/**/* - -# (requires option 2 above): Personal Schemes -# -#!xcuserdata/**/xcschemes/* - -#### -# XCode 4 workspaces - more detailed -# -# Workspaces are important! They are a core feature of Xcode - don't exclude them :) -# -# Workspace layout is quite spammy. For reference: -# -# /(root)/ -# /(project-name).xcodeproj/ -# project.pbxproj -# /project.xcworkspace/ -# contents.xcworkspacedata -# /xcuserdata/ -# /(your name)/xcuserdatad/ -# UserInterfaceState.xcuserstate -# /xcsshareddata/ -# /xcschemes/ -# (shared scheme name).xcscheme -# /xcuserdata/ -# /(your name)/xcuserdatad/ -# (private scheme).xcscheme -# xcschememanagement.plist -# -# - -project.xcworkspace - -#### -# Xcode 4 - Deprecated classes -# -# Allegedly, if you manually "deprecate" your classes, they get moved here. -# -# We're using source-control, so this is a "feature" that we do not want! - +## Other *.moved-aside - -#### -# UNKNOWN: recommended by others, but I can't discover what these files are -# -# ...none. Everything is now explained. \ No newline at end of file +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ +!Podfile +!Podfile.lock + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts +#!Cartfile +#!Cartfile.private +#!Cartfile.resolved + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +<<<<<<< HEAD +fastlane/test_output +README.md +DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Contents.json +======= +fastlane/test_output +>>>>>>> swift4 diff --git a/DailyDozen/.DS_Store b/DailyDozen/.DS_Store new file mode 100644 index 00000000..7c30540c Binary files /dev/null and b/DailyDozen/.DS_Store differ diff --git a/DailyDozen/.swiftlint.yml b/DailyDozen/.swiftlint.yml new file mode 100644 index 00000000..b4b90a46 --- /dev/null +++ b/DailyDozen/.swiftlint.yml @@ -0,0 +1,12 @@ +identifier_name: + min_length: # not possible to disable this partial rule, so set it to zero + warning: 0 + error: 0 +disabled_rules: + - file_length + - line_length + - function_body_length + - todo + - type_body_length +excluded: + - Pods \ No newline at end of file diff --git a/DailyDozen/DailyDozen.xcodeproj/project.pbxproj b/DailyDozen/DailyDozen.xcodeproj/project.pbxproj new file mode 100644 index 00000000..fedefddc --- /dev/null +++ b/DailyDozen/DailyDozen.xcodeproj/project.pbxproj @@ -0,0 +1,1010 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + BED5BB460CE3AEE8CF585899 /* Pods_DailyDozen.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F514690D725E67AD54735EB9 /* Pods_DailyDozen.framework */; }; + CF12918F1FC42DD300D1C17E /* DateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF12918E1FC42DD300D1C17E /* DateCell.swift */; }; + CF1393111FE0046800DD8679 /* Reminder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CF1393101FE0046700DD8679 /* Reminder.storyboard */; }; + CF1393151FE0080900DD8679 /* ReminderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1393141FE0080900DD8679 /* ReminderViewController.swift */; }; + CF17C8A71FAA019600367BF8 /* DetailsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF17C8A61FAA019600367BF8 /* DetailsSection.swift */; }; + CF22F07A1FA88D2200F25B70 /* DetailsDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF22F0791FA88D2200F25B70 /* DetailsDataProvider.swift */; }; + CF22F07F1FA8ACE000F25B70 /* Details.plist in Resources */ = {isa = PBXBuildFile; fileRef = CF22F07E1FA8ACE000F25B70 /* Details.plist */; }; + CF2680021FB477AD004A6200 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF2680011FB477AD004A6200 /* Date.swift */; }; + CF2FA9941FA87B2E003508F8 /* Details.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CF2FA9931FA87B2E003508F8 /* Details.storyboard */; }; + CF2FA9961FA87F0C003508F8 /* DetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF2FA9951FA87F0C003508F8 /* DetailsViewController.swift */; }; + CF4902951FD91412000EAB68 /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4902941FD91412000EAB68 /* ChartView.swift */; }; + CF4902971FD917D4000EAB68 /* ControlPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4902961FD917D4000EAB68 /* ControlPanel.swift */; }; + CF49029B1FD91F01000EAB68 /* ServingsHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF49029A1FD91F01000EAB68 /* ServingsHistoryViewModel.swift */; }; + CF4DECF71F9DF2A500DA094B /* ServingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4DECF61F9DF2A500DA094B /* ServingsCell.swift */; }; + CF4DECF91F9DF82900DA094B /* ServingsDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4DECF81F9DF82900DA094B /* ServingsDataProvider.swift */; }; + CF55BC4F2020450D00003D0D /* AlertBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF55BC4E2020450D00003D0D /* AlertBuilder.swift */; }; + CF5B28B11FA0A2C500D57A48 /* StateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF5B28B01FA0A2C500D57A48 /* StateCell.swift */; }; + CF64DF271FB5BAC400B4D124 /* SizesHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF64DF261FB5BAC400B4D124 /* SizesHeader.xib */; }; + CF64DF2C1FB5CD5100B4D124 /* TypesHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF64DF2B1FB5CD5100B4D124 /* TypesHeader.xib */; }; + CF64DF311FB5DFCF00B4D124 /* SizesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF64DF301FB5DFCF00B4D124 /* SizesCell.swift */; }; + CF64DF331FB5E03C00B4D124 /* TypesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF64DF321FB5E03C00B4D124 /* TypesCell.swift */; }; + CF6E6D351FB1B4FE00B177E7 /* Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6E6D341FB1B4FE00B177E7 /* Detail.swift */; }; + CF6E6D391FB1B8E300B177E7 /* TextsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6E6D381FB1B8E300B177E7 /* TextsProvider.swift */; }; + CF6E6D3C1FB1BEC400B177E7 /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6E6D3B1FB1BEC400B177E7 /* DetailViewModel.swift */; }; + CF9051512022F71B00F4C1F2 /* dr_greger.png in Resources */ = {isa = PBXBuildFile; fileRef = CF9051502022F71B00F4C1F2 /* dr_greger.png */; }; + CF987C281F9E157300D4893E /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF987C271F9E157300D4893E /* Item.swift */; }; + CF9E03201FBD9FA80084BC88 /* ItemHistory.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CF9E031F1FBD9FA80084BC88 /* ItemHistory.storyboard */; }; + CF9E03231FBD9FDE0084BC88 /* ItemHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF9E03221FBD9FDE0084BC88 /* ItemHistoryViewController.swift */; }; + CF9EC1C0202196B100C7B3CA /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF9EC1BF202196B100C7B3CA /* Fonts.swift */; }; + CF9EC1C32021D37000C7B3CA /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF9EC1C22021D37000C7B3CA /* MenuItem.swift */; }; + CFA3E8F91FC58D460092199D /* ServingsHistory.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFA3E8F81FC58D460092199D /* ServingsHistory.storyboard */; }; + CFA3E8FC1FC58E6B0092199D /* ServingsHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFA3E8FB1FC58E6B0092199D /* ServingsHistoryViewController.swift */; }; + CFA9CDF21FBAF653000468CA /* VitaminsHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = CFA9CDF11FBAF653000468CA /* VitaminsHeader.xib */; }; + CFAAB3721F9F3BC600B24748 /* Doze.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFAAB3711F9F3BC600B24748 /* Doze.swift */; }; + CFAAB3741F9F3E1200B24748 /* RealmConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFAAB3731F9F3E1200B24748 /* RealmConfig.swift */; }; + CFAAB3771F9F3FDF00B24748 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFAAB3761F9F3FDF00B24748 /* URL.swift */; }; + CFAAB37A1F9F4A8000B24748 /* DozeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFAAB3791F9F4A8000B24748 /* DozeViewModel.swift */; }; + CFB5568C1FD180C3003B5813 /* Report.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFB5568B1FD180C3003B5813 /* Report.swift */; }; + CFBEE2301FDE8D1E005C0F45 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFBEE22F1FDE8D1E005C0F45 /* About.storyboard */; }; + CFBEE2321FDE963D005C0F45 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBEE2311FDE963D005C0F45 /* AboutViewController.swift */; }; + CFBEE2341FDE9A57005C0F45 /* RoundedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBEE2331FDE9A57005C0F45 /* RoundedView.swift */; }; + CFC060931FBC57C800A901A5 /* ServingsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFC060921FBC57C800A901A5 /* ServingsSection.swift */; }; + CFC4A84D1F975A3900B5AC75 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFC4A84C1F975A3900B5AC75 /* MenuViewController.swift */; }; + CFC4A84F1F9761FF00B5AC75 /* PagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFC4A84E1F9761FF00B5AC75 /* PagerViewController.swift */; }; + CFC4A8541F9776A500B5AC75 /* ServingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFC4A8531F9776A500B5AC75 /* ServingsViewController.swift */; }; + CFCE5FCB1F978007005C672B /* Servings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFCE5FCA1F978007005C672B /* Servings.storyboard */; }; + CFD1BA331FCD24920057F549 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFD1BA321FCD24920057F549 /* RoundedButton.swift */; }; + CFE667291F98CF860037D1A2 /* Pager.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFE667281F98CF860037D1A2 /* Pager.storyboard */; }; + CFE6983C1F974B9700CD7905 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE6983B1F974B9700CD7905 /* AppDelegate.swift */; }; + CFE6983E1F974B9700CD7905 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE6983D1F974B9700CD7905 /* MainViewController.swift */; }; + CFE698411F974B9700CD7905 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFE6983F1F974B9700CD7905 /* Main.storyboard */; }; + CFE698431F974B9700CD7905 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CFE698421F974B9700CD7905 /* Assets.xcassets */; }; + CFE698461F974B9700CD7905 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFE698441F974B9700CD7905 /* LaunchScreen.storyboard */; }; + CFE793571FA756E40084F075 /* RealmProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE793561FA756E40084F075 /* RealmProvider.swift */; }; + CFECDFF8201EF7C1003E8572 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFECDFF7201EF7C1003E8572 /* Colors.swift */; }; + CFF117731F97530300BFE73A /* Menu.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CFF117721F97530300BFE73A /* Menu.storyboard */; }; + CFF72AFB1FB9A499001CC2E9 /* UnitsType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF72AFA1FB9A499001CC2E9 /* UnitsType.swift */; }; + CFF72AFE1FB9BAAA001CC2E9 /* LinkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF72AFD1FB9BAAA001CC2E9 /* LinkService.swift */; }; + CFF72B001FB9BB70001CC2E9 /* LinkSettings.plist in Resources */ = {isa = PBXBuildFile; fileRef = CFF72AFF1FB9BB70001CC2E9 /* LinkSettings.plist */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 7D4AA01331AD98C99ADB2BC0 /* Pods-DailyDozen.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DailyDozen.release.xcconfig"; path = "Pods/Target Support Files/Pods-DailyDozen/Pods-DailyDozen.release.xcconfig"; sourceTree = ""; }; + CF12918E1FC42DD300D1C17E /* DateCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCell.swift; sourceTree = ""; }; + CF1393101FE0046700DD8679 /* Reminder.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Reminder.storyboard; sourceTree = ""; }; + CF1393141FE0080900DD8679 /* ReminderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderViewController.swift; sourceTree = ""; }; + CF17C8A61FAA019600367BF8 /* DetailsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsSection.swift; sourceTree = ""; }; + CF22F0791FA88D2200F25B70 /* DetailsDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsDataProvider.swift; sourceTree = ""; }; + CF22F07E1FA8ACE000F25B70 /* Details.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Details.plist; sourceTree = ""; }; + CF2680011FB477AD004A6200 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; + CF2FA9931FA87B2E003508F8 /* Details.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Details.storyboard; sourceTree = ""; }; + CF2FA9951FA87F0C003508F8 /* DetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsViewController.swift; sourceTree = ""; }; + CF4902941FD91412000EAB68 /* ChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = ""; }; + CF4902961FD917D4000EAB68 /* ControlPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlPanel.swift; sourceTree = ""; }; + CF49029A1FD91F01000EAB68 /* ServingsHistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsHistoryViewModel.swift; sourceTree = ""; }; + CF4DECF61F9DF2A500DA094B /* ServingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsCell.swift; sourceTree = ""; }; + CF4DECF81F9DF82900DA094B /* ServingsDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsDataProvider.swift; sourceTree = ""; }; + CF55BC4E2020450D00003D0D /* AlertBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertBuilder.swift; sourceTree = ""; }; + CF5B28B01FA0A2C500D57A48 /* StateCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateCell.swift; sourceTree = ""; }; + CF64DF261FB5BAC400B4D124 /* SizesHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SizesHeader.xib; sourceTree = ""; }; + CF64DF2B1FB5CD5100B4D124 /* TypesHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TypesHeader.xib; sourceTree = ""; }; + CF64DF301FB5DFCF00B4D124 /* SizesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizesCell.swift; sourceTree = ""; }; + CF64DF321FB5E03C00B4D124 /* TypesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypesCell.swift; sourceTree = ""; }; + CF6E6D341FB1B4FE00B177E7 /* Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Detail.swift; sourceTree = ""; }; + CF6E6D381FB1B8E300B177E7 /* TextsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsProvider.swift; sourceTree = ""; }; + CF6E6D3B1FB1BEC400B177E7 /* DetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewModel.swift; sourceTree = ""; }; + CF9051502022F71B00F4C1F2 /* dr_greger.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = dr_greger.png; path = DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/dr_greger.png; sourceTree = SOURCE_ROOT; }; + CF987C271F9E157300D4893E /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + CF9E031F1FBD9FA80084BC88 /* ItemHistory.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ItemHistory.storyboard; sourceTree = ""; }; + CF9E03221FBD9FDE0084BC88 /* ItemHistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemHistoryViewController.swift; sourceTree = ""; }; + CF9EC1BF202196B100C7B3CA /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; + CF9EC1C22021D37000C7B3CA /* MenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = ""; }; + CFA3E8F81FC58D460092199D /* ServingsHistory.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ServingsHistory.storyboard; sourceTree = ""; }; + CFA3E8FB1FC58E6B0092199D /* ServingsHistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsHistoryViewController.swift; sourceTree = ""; }; + CFA9CDF11FBAF653000468CA /* VitaminsHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VitaminsHeader.xib; sourceTree = ""; }; + CFAAB3711F9F3BC600B24748 /* Doze.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Doze.swift; sourceTree = ""; }; + CFAAB3731F9F3E1200B24748 /* RealmConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmConfig.swift; sourceTree = ""; }; + CFAAB3761F9F3FDF00B24748 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; + CFAAB3791F9F4A8000B24748 /* DozeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DozeViewModel.swift; sourceTree = ""; }; + CFB5568B1FD180C3003B5813 /* Report.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Report.swift; sourceTree = ""; }; + CFBEE22F1FDE8D1E005C0F45 /* About.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = About.storyboard; sourceTree = ""; }; + CFBEE2311FDE963D005C0F45 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; + CFBEE2331FDE9A57005C0F45 /* RoundedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedView.swift; sourceTree = ""; }; + CFC060921FBC57C800A901A5 /* ServingsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsSection.swift; sourceTree = ""; }; + CFC4A84C1F975A3900B5AC75 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; + CFC4A84E1F9761FF00B5AC75 /* PagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerViewController.swift; sourceTree = ""; }; + CFC4A8531F9776A500B5AC75 /* ServingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServingsViewController.swift; sourceTree = ""; }; + CFCE5FCA1F978007005C672B /* Servings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Servings.storyboard; sourceTree = ""; }; + CFD1BA321FCD24920057F549 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; + CFE667281F98CF860037D1A2 /* Pager.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Pager.storyboard; sourceTree = ""; }; + CFE698381F974B9700CD7905 /* DailyDozen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DailyDozen.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CFE6983B1F974B9700CD7905 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + CFE6983D1F974B9700CD7905 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + CFE698401F974B9700CD7905 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + CFE698421F974B9700CD7905 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CFE698451F974B9700CD7905 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + CFE698471F974B9700CD7905 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CFE793561FA756E40084F075 /* RealmProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmProvider.swift; sourceTree = ""; }; + CFECDFF7201EF7C1003E8572 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; + CFF117721F97530300BFE73A /* Menu.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Menu.storyboard; sourceTree = ""; }; + CFF72AFA1FB9A499001CC2E9 /* UnitsType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsType.swift; sourceTree = ""; }; + CFF72AFD1FB9BAAA001CC2E9 /* LinkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkService.swift; sourceTree = ""; }; + CFF72AFF1FB9BB70001CC2E9 /* LinkSettings.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = LinkSettings.plist; sourceTree = ""; }; + DA1790B64ED37B6DB70223BE /* Pods-DailyDozen.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DailyDozen.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DailyDozen/Pods-DailyDozen.debug.xcconfig"; sourceTree = ""; }; + F514690D725E67AD54735EB9 /* Pods_DailyDozen.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DailyDozen.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CFE698351F974B9700CD7905 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BED5BB460CE3AEE8CF585899 /* Pods_DailyDozen.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 68646D18FC1A63146B00CB7C /* Frameworks */ = { + isa = PBXGroup; + children = ( + F514690D725E67AD54735EB9 /* Pods_DailyDozen.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + CF12918D1FC42DB000D1C17E /* Views */ = { + isa = PBXGroup; + children = ( + CF12918E1FC42DD300D1C17E /* DateCell.swift */, + ); + path = Views; + sourceTree = ""; + }; + CF13930F1FE0042800DD8679 /* Reminder */ = { + isa = PBXGroup; + children = ( + CF1393131FE007E400DD8679 /* Controllers */, + CF1393121FE0046C00DD8679 /* Storyboards */, + ); + path = Reminder; + sourceTree = ""; + }; + CF1393121FE0046C00DD8679 /* Storyboards */ = { + isa = PBXGroup; + children = ( + CF1393101FE0046700DD8679 /* Reminder.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CF1393131FE007E400DD8679 /* Controllers */ = { + isa = PBXGroup; + children = ( + CF1393141FE0080900DD8679 /* ReminderViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CF17C8A51FAA016D00367BF8 /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + CF17C8A61FAA019600367BF8 /* DetailsSection.swift */, + CFF72AFA1FB9A499001CC2E9 /* UnitsType.swift */, + ); + path = SupportingFiles; + sourceTree = ""; + }; + CF22F07D1FA8ACC300F25B70 /* Texts */ = { + isa = PBXGroup; + children = ( + CF6E6D381FB1B8E300B177E7 /* TextsProvider.swift */, + CF22F07E1FA8ACE000F25B70 /* Details.plist */, + ); + path = Texts; + sourceTree = ""; + }; + CF2FA9911FA87AB5003508F8 /* Details */ = { + isa = PBXGroup; + children = ( + CF6E6D321FB1B4DD00B177E7 /* Models */, + CF64DF2D1FB5DC8300B4D124 /* View */, + CF6E6D3A1FB1BE9500B177E7 /* ViewModels */, + CF2FA9971FA87F31003508F8 /* Controllers */, + CF2FA9921FA87AEC003508F8 /* Storyboards */, + CF17C8A51FAA016D00367BF8 /* SupportingFiles */, + ); + path = Details; + sourceTree = ""; + }; + CF2FA9921FA87AEC003508F8 /* Storyboards */ = { + isa = PBXGroup; + children = ( + CF2FA9931FA87B2E003508F8 /* Details.storyboard */, + CF64DF261FB5BAC400B4D124 /* SizesHeader.xib */, + CF64DF2B1FB5CD5100B4D124 /* TypesHeader.xib */, + ); + path = Storyboards; + sourceTree = ""; + }; + CF2FA9971FA87F31003508F8 /* Controllers */ = { + isa = PBXGroup; + children = ( + CF2FA9951FA87F0C003508F8 /* DetailsViewController.swift */, + CF22F0791FA88D2200F25B70 /* DetailsDataProvider.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CF4902931FD913E9000EAB68 /* Views */ = { + isa = PBXGroup; + children = ( + CF4902941FD91412000EAB68 /* ChartView.swift */, + CF4902961FD917D4000EAB68 /* ControlPanel.swift */, + ); + path = Views; + sourceTree = ""; + }; + CF4902981FD91EB9000EAB68 /* ViewModels */ = { + isa = PBXGroup; + children = ( + CF49029A1FD91F01000EAB68 /* ServingsHistoryViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + CF4DECF51F9DF27800DA094B /* Views */ = { + isa = PBXGroup; + children = ( + CF4DECF61F9DF2A500DA094B /* ServingsCell.swift */, + CF5B28B01FA0A2C500D57A48 /* StateCell.swift */, + ); + path = Views; + sourceTree = ""; + }; + CF64DF2D1FB5DC8300B4D124 /* View */ = { + isa = PBXGroup; + children = ( + CF64DF301FB5DFCF00B4D124 /* SizesCell.swift */, + CF64DF321FB5E03C00B4D124 /* TypesCell.swift */, + ); + path = View; + sourceTree = ""; + }; + CF6E6D321FB1B4DD00B177E7 /* Models */ = { + isa = PBXGroup; + children = ( + CF6E6D341FB1B4FE00B177E7 /* Detail.swift */, + ); + path = Models; + sourceTree = ""; + }; + CF6E6D3A1FB1BE9500B177E7 /* ViewModels */ = { + isa = PBXGroup; + children = ( + CF6E6D3B1FB1BEC400B177E7 /* DetailViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + CF90514F2022F01A00F4C1F2 /* Attachments */ = { + isa = PBXGroup; + children = ( + CF9051502022F71B00F4C1F2 /* dr_greger.png */, + ); + path = Attachments; + sourceTree = ""; + }; + CF987C261F9E149000D4893E /* Models */ = { + isa = PBXGroup; + children = ( + CF987C271F9E157300D4893E /* Item.swift */, + CFAAB3711F9F3BC600B24748 /* Doze.swift */, + ); + path = Models; + sourceTree = ""; + }; + CF9E031D1FBD9F540084BC88 /* ItemHistory */ = { + isa = PBXGroup; + children = ( + CF12918D1FC42DB000D1C17E /* Views */, + CF9E03211FBD9FC50084BC88 /* Controllers */, + CF9E031E1FBD9F800084BC88 /* Storyboards */, + ); + path = ItemHistory; + sourceTree = ""; + }; + CF9E031E1FBD9F800084BC88 /* Storyboards */ = { + isa = PBXGroup; + children = ( + CF9E031F1FBD9FA80084BC88 /* ItemHistory.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CF9E03211FBD9FC50084BC88 /* Controllers */ = { + isa = PBXGroup; + children = ( + CF9E03221FBD9FDE0084BC88 /* ItemHistoryViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CF9EC1C12021D30800C7B3CA /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + CF9EC1C22021D37000C7B3CA /* MenuItem.swift */, + ); + path = SupportingFiles; + sourceTree = ""; + }; + CFA3E8F61FC58CD90092199D /* ServingsHistory */ = { + isa = PBXGroup; + children = ( + CFB5568A1FD18086003B5813 /* Models */, + CF4902981FD91EB9000EAB68 /* ViewModels */, + CF4902931FD913E9000EAB68 /* Views */, + CFA3E8FA1FC58E4F0092199D /* Controllers */, + CFA3E8F71FC58D160092199D /* Storyboards */, + ); + path = ServingsHistory; + sourceTree = ""; + }; + CFA3E8F71FC58D160092199D /* Storyboards */ = { + isa = PBXGroup; + children = ( + CFA3E8F81FC58D460092199D /* ServingsHistory.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CFA3E8FA1FC58E4F0092199D /* Controllers */ = { + isa = PBXGroup; + children = ( + CFA3E8FB1FC58E6B0092199D /* ServingsHistoryViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CFAAB3751F9F3FCD00B24748 /* Extensions */ = { + isa = PBXGroup; + children = ( + CFAAB3761F9F3FDF00B24748 /* URL.swift */, + CF2680011FB477AD004A6200 /* Date.swift */, + CFECDFF7201EF7C1003E8572 /* Colors.swift */, + CF9EC1BF202196B100C7B3CA /* Fonts.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + CFAAB3781F9F4A6000B24748 /* ViewModel */ = { + isa = PBXGroup; + children = ( + CFAAB3791F9F4A8000B24748 /* DozeViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + CFAAB37B1F9F4B0D00B24748 /* Realm */ = { + isa = PBXGroup; + children = ( + CFAAB3731F9F3E1200B24748 /* RealmConfig.swift */, + CFE793561FA756E40084F075 /* RealmProvider.swift */, + ); + path = Realm; + sourceTree = ""; + }; + CFB5568A1FD18086003B5813 /* Models */ = { + isa = PBXGroup; + children = ( + CFB5568B1FD180C3003B5813 /* Report.swift */, + ); + path = Models; + sourceTree = ""; + }; + CFBEE22A1FDE8CBA005C0F45 /* About */ = { + isa = PBXGroup; + children = ( + CFBEE22C1FDE8CE0005C0F45 /* Controllers */, + CFBEE22B1FDE8CD5005C0F45 /* Storyboards */, + ); + path = About; + sourceTree = ""; + }; + CFBEE22B1FDE8CD5005C0F45 /* Storyboards */ = { + isa = PBXGroup; + children = ( + CFBEE22F1FDE8D1E005C0F45 /* About.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CFBEE22C1FDE8CE0005C0F45 /* Controllers */ = { + isa = PBXGroup; + children = ( + CFBEE2311FDE963D005C0F45 /* AboutViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CFC060911FBC579700A901A5 /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + CFC060921FBC57C800A901A5 /* ServingsSection.swift */, + ); + path = SupportingFiles; + sourceTree = ""; + }; + CFC4A84B1F975A0C00B5AC75 /* Controllers */ = { + isa = PBXGroup; + children = ( + CFC4A84C1F975A3900B5AC75 /* MenuViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CFC4A8501F97620400B5AC75 /* Controllers */ = { + isa = PBXGroup; + children = ( + CFC4A84E1F9761FF00B5AC75 /* PagerViewController.swift */, + CFC4A8531F9776A500B5AC75 /* ServingsViewController.swift */, + CF4DECF81F9DF82900DA094B /* ServingsDataProvider.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CFD1BA311FCD24220057F549 /* Views */ = { + isa = PBXGroup; + children = ( + CFBEE2331FDE9A57005C0F45 /* RoundedView.swift */, + CFD1BA321FCD24920057F549 /* RoundedButton.swift */, + ); + path = Views; + sourceTree = ""; + }; + CFE6982F1F974B9700CD7905 = { + isa = PBXGroup; + children = ( + CFE6983A1F974B9700CD7905 /* DailyDozen */, + CFE698391F974B9700CD7905 /* Products */, + D0E09D1F35AF7D3FF1EA14DC /* Pods */, + 68646D18FC1A63146B00CB7C /* Frameworks */, + ); + sourceTree = ""; + }; + CFE698391F974B9700CD7905 /* Products */ = { + isa = PBXGroup; + children = ( + CFE698381F974B9700CD7905 /* DailyDozen.app */, + ); + name = Products; + sourceTree = ""; + }; + CFE6983A1F974B9700CD7905 /* DailyDozen */ = { + isa = PBXGroup; + children = ( + CFE6984F1F974EFF00CD7905 /* App */, + CFF117711F9752CA00BFE73A /* Menu */, + CFF117751F9753B700BFE73A /* Servings */, + CF2FA9911FA87AB5003508F8 /* Details */, + CF9E031D1FBD9F540084BC88 /* ItemHistory */, + CFA3E8F61FC58CD90092199D /* ServingsHistory */, + CFBEE22A1FDE8CBA005C0F45 /* About */, + CF13930F1FE0042800DD8679 /* Reminder */, + ); + path = DailyDozen; + sourceTree = ""; + }; + CFE6984F1F974EFF00CD7905 /* App */ = { + isa = PBXGroup; + children = ( + CFD1BA311FCD24220057F549 /* Views */, + CFE698501F974F0F00CD7905 /* Controllers */, + CFAAB37B1F9F4B0D00B24748 /* Realm */, + CFF72AFC1FB9BA4F001CC2E9 /* Services */, + CF22F07D1FA8ACC300F25B70 /* Texts */, + CFAAB3751F9F3FCD00B24748 /* Extensions */, + CFE698531F974F6300CD7905 /* Storyboards */, + CFE698511F974F1800CD7905 /* SupportingFiles */, + CFE698521F974F4E00CD7905 /* Resources */, + ); + path = App; + sourceTree = ""; + }; + CFE698501F974F0F00CD7905 /* Controllers */ = { + isa = PBXGroup; + children = ( + CFE6983D1F974B9700CD7905 /* MainViewController.swift */, + CF55BC4E2020450D00003D0D /* AlertBuilder.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + CFE698511F974F1800CD7905 /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + CF90514F2022F01A00F4C1F2 /* Attachments */, + CFE6983B1F974B9700CD7905 /* AppDelegate.swift */, + CFE698471F974B9700CD7905 /* Info.plist */, + ); + path = SupportingFiles; + sourceTree = ""; + }; + CFE698521F974F4E00CD7905 /* Resources */ = { + isa = PBXGroup; + children = ( + CFE698421F974B9700CD7905 /* Assets.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + CFE698531F974F6300CD7905 /* Storyboards */ = { + isa = PBXGroup; + children = ( + CFE6983F1F974B9700CD7905 /* Main.storyboard */, + CFE698441F974B9700CD7905 /* LaunchScreen.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CFF117711F9752CA00BFE73A /* Menu */ = { + isa = PBXGroup; + children = ( + CFC4A84B1F975A0C00B5AC75 /* Controllers */, + CFF117741F97533F00BFE73A /* Storyboards */, + CF9EC1C12021D30800C7B3CA /* SupportingFiles */, + ); + path = Menu; + sourceTree = ""; + }; + CFF117741F97533F00BFE73A /* Storyboards */ = { + isa = PBXGroup; + children = ( + CFF117721F97530300BFE73A /* Menu.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + CFF117751F9753B700BFE73A /* Servings */ = { + isa = PBXGroup; + children = ( + CF987C261F9E149000D4893E /* Models */, + CF4DECF51F9DF27800DA094B /* Views */, + CFAAB3781F9F4A6000B24748 /* ViewModel */, + CFC4A8501F97620400B5AC75 /* Controllers */, + CFF117781F97543900BFE73A /* Storyboards */, + CFC060911FBC579700A901A5 /* SupportingFiles */, + ); + path = Servings; + sourceTree = ""; + }; + CFF117781F97543900BFE73A /* Storyboards */ = { + isa = PBXGroup; + children = ( + CFE667281F98CF860037D1A2 /* Pager.storyboard */, + CFCE5FCA1F978007005C672B /* Servings.storyboard */, + CFA9CDF11FBAF653000468CA /* VitaminsHeader.xib */, + ); + path = Storyboards; + sourceTree = ""; + }; + CFF72AFC1FB9BA4F001CC2E9 /* Services */ = { + isa = PBXGroup; + children = ( + CFF72AFD1FB9BAAA001CC2E9 /* LinkService.swift */, + CFF72AFF1FB9BB70001CC2E9 /* LinkSettings.plist */, + ); + path = Services; + sourceTree = ""; + }; + D0E09D1F35AF7D3FF1EA14DC /* Pods */ = { + isa = PBXGroup; + children = ( + DA1790B64ED37B6DB70223BE /* Pods-DailyDozen.debug.xcconfig */, + 7D4AA01331AD98C99ADB2BC0 /* Pods-DailyDozen.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CFE698371F974B9700CD7905 /* DailyDozen */ = { + isa = PBXNativeTarget; + buildConfigurationList = CFE6984A1F974B9700CD7905 /* Build configuration list for PBXNativeTarget "DailyDozen" */; + buildPhases = ( + 203F4C6C21B11760680A8B7B /* [CP] Check Pods Manifest.lock */, + CFE698341F974B9700CD7905 /* Sources */, + CFE698351F974B9700CD7905 /* Frameworks */, + CFE698361F974B9700CD7905 /* Resources */, + CFE6984E1F974CE400CD7905 /* ShellScript */, + B928040A54DABDDC50DB0D10 /* [CP] Embed Pods Frameworks */, + 70F25E4D8F3A74B6E49D3041 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DailyDozen; + productName = DailyDozen; + productReference = CFE698381F974B9700CD7905 /* DailyDozen.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CFE698301F974B9700CD7905 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0900; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = Nutritionfacts.org; + TargetAttributes = { + CFE698371F974B9700CD7905 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = CFE698331F974B9700CD7905 /* Build configuration list for PBXProject "DailyDozen" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CFE6982F1F974B9700CD7905; + productRefGroup = CFE698391F974B9700CD7905 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CFE698371F974B9700CD7905 /* DailyDozen */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CFE698361F974B9700CD7905 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CFE667291F98CF860037D1A2 /* Pager.storyboard in Resources */, + CF2FA9941FA87B2E003508F8 /* Details.storyboard in Resources */, + CFBEE2301FDE8D1E005C0F45 /* About.storyboard in Resources */, + CF9E03201FBD9FA80084BC88 /* ItemHistory.storyboard in Resources */, + CFE698461F974B9700CD7905 /* LaunchScreen.storyboard in Resources */, + CFCE5FCB1F978007005C672B /* Servings.storyboard in Resources */, + CFE698431F974B9700CD7905 /* Assets.xcassets in Resources */, + CF22F07F1FA8ACE000F25B70 /* Details.plist in Resources */, + CF64DF2C1FB5CD5100B4D124 /* TypesHeader.xib in Resources */, + CF9051512022F71B00F4C1F2 /* dr_greger.png in Resources */, + CFA9CDF21FBAF653000468CA /* VitaminsHeader.xib in Resources */, + CFA3E8F91FC58D460092199D /* ServingsHistory.storyboard in Resources */, + CF64DF271FB5BAC400B4D124 /* SizesHeader.xib in Resources */, + CFE698411F974B9700CD7905 /* Main.storyboard in Resources */, + CFF72B001FB9BB70001CC2E9 /* LinkSettings.plist in Resources */, + CF1393111FE0046800DD8679 /* Reminder.storyboard in Resources */, + CFF117731F97530300BFE73A /* Menu.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 203F4C6C21B11760680A8B7B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-DailyDozen-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 70F25E4D8F3A74B6E49D3041 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DailyDozen/Pods-DailyDozen-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + B928040A54DABDDC50DB0D10 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-DailyDozen/Pods-DailyDozen-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/ActiveLabel/ActiveLabel.framework", + "${BUILT_PRODUCTS_DIR}/Charts/Charts.framework", + "${BUILT_PRODUCTS_DIR}/FSCalendar/FSCalendar.framework", + "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework", + "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework", + "${BUILT_PRODUCTS_DIR}/SimpleAnimation/SimpleAnimation.framework", + "${BUILT_PRODUCTS_DIR}/UICheckbox.Swift/UICheckbox_Swift.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ActiveLabel.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Charts.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FSCalendar.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SimpleAnimation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UICheckbox_Swift.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DailyDozen/Pods-DailyDozen-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + CFE6984E1F974CE400CD7905 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CFE698341F974B9700CD7905 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CFA3E8FC1FC58E6B0092199D /* ServingsHistoryViewController.swift in Sources */, + CFF72AFE1FB9BAAA001CC2E9 /* LinkService.swift in Sources */, + CFC4A8541F9776A500B5AC75 /* ServingsViewController.swift in Sources */, + CF4902951FD91412000EAB68 /* ChartView.swift in Sources */, + CFAAB3721F9F3BC600B24748 /* Doze.swift in Sources */, + CF22F07A1FA88D2200F25B70 /* DetailsDataProvider.swift in Sources */, + CFE6983E1F974B9700CD7905 /* MainViewController.swift in Sources */, + CF64DF331FB5E03C00B4D124 /* TypesCell.swift in Sources */, + CFAAB3771F9F3FDF00B24748 /* URL.swift in Sources */, + CF6E6D351FB1B4FE00B177E7 /* Detail.swift in Sources */, + CF1393151FE0080900DD8679 /* ReminderViewController.swift in Sources */, + CF6E6D391FB1B8E300B177E7 /* TextsProvider.swift in Sources */, + CFC4A84D1F975A3900B5AC75 /* MenuViewController.swift in Sources */, + CF6E6D3C1FB1BEC400B177E7 /* DetailViewModel.swift in Sources */, + CFE6983C1F974B9700CD7905 /* AppDelegate.swift in Sources */, + CF64DF311FB5DFCF00B4D124 /* SizesCell.swift in Sources */, + CFB5568C1FD180C3003B5813 /* Report.swift in Sources */, + CF987C281F9E157300D4893E /* Item.swift in Sources */, + CF9EC1C0202196B100C7B3CA /* Fonts.swift in Sources */, + CFBEE2341FDE9A57005C0F45 /* RoundedView.swift in Sources */, + CF9EC1C32021D37000C7B3CA /* MenuItem.swift in Sources */, + CF49029B1FD91F01000EAB68 /* ServingsHistoryViewModel.swift in Sources */, + CF2FA9961FA87F0C003508F8 /* DetailsViewController.swift in Sources */, + CFECDFF8201EF7C1003E8572 /* Colors.swift in Sources */, + CF4DECF71F9DF2A500DA094B /* ServingsCell.swift in Sources */, + CFE793571FA756E40084F075 /* RealmProvider.swift in Sources */, + CFAAB37A1F9F4A8000B24748 /* DozeViewModel.swift in Sources */, + CF4902971FD917D4000EAB68 /* ControlPanel.swift in Sources */, + CFC060931FBC57C800A901A5 /* ServingsSection.swift in Sources */, + CF5B28B11FA0A2C500D57A48 /* StateCell.swift in Sources */, + CFBEE2321FDE963D005C0F45 /* AboutViewController.swift in Sources */, + CF12918F1FC42DD300D1C17E /* DateCell.swift in Sources */, + CF2680021FB477AD004A6200 /* Date.swift in Sources */, + CFF72AFB1FB9A499001CC2E9 /* UnitsType.swift in Sources */, + CFC4A84F1F9761FF00B5AC75 /* PagerViewController.swift in Sources */, + CFD1BA331FCD24920057F549 /* RoundedButton.swift in Sources */, + CF9E03231FBD9FDE0084BC88 /* ItemHistoryViewController.swift in Sources */, + CF4DECF91F9DF82900DA094B /* ServingsDataProvider.swift in Sources */, + CF17C8A71FAA019600367BF8 /* DetailsSection.swift in Sources */, + CF55BC4F2020450D00003D0D /* AlertBuilder.swift in Sources */, + CFAAB3741F9F3E1200B24748 /* RealmConfig.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + CFE6983F1F974B9700CD7905 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CFE698401F974B9700CD7905 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + CFE698441F974B9700CD7905 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CFE698451F974B9700CD7905 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CFE698481F974B9700CD7905 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CFE698491F974B9700CD7905 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CFE6984B1F974B9700CD7905 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DA1790B64ED37B6DB70223BE /* Pods-DailyDozen.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 2MG57YUZL5; + INFOPLIST_FILE = "$(SRCROOT)/DailyDozen/App/SupportingFiles/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nutritionfacts.dailydozen; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + CFE6984C1F974B9700CD7905 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7D4AA01331AD98C99ADB2BC0 /* Pods-DailyDozen.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 2MG57YUZL5; + INFOPLIST_FILE = "$(SRCROOT)/DailyDozen/App/SupportingFiles/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nutritionfacts.dailydozen; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CFE698331F974B9700CD7905 /* Build configuration list for PBXProject "DailyDozen" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CFE698481F974B9700CD7905 /* Debug */, + CFE698491F974B9700CD7905 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CFE6984A1F974B9700CD7905 /* Build configuration list for PBXNativeTarget "DailyDozen" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CFE6984B1F974B9700CD7905 /* Debug */, + CFE6984C1F974B9700CD7905 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CFE698301F974B9700CD7905 /* Project object */; +} diff --git a/DailyDozen/DailyDozen.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DailyDozen/DailyDozen.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b317ed3a --- /dev/null +++ b/DailyDozen/DailyDozen.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DailyDozen/DailyDozen.xcworkspace/contents.xcworkspacedata b/DailyDozen/DailyDozen.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..cd315802 --- /dev/null +++ b/DailyDozen/DailyDozen.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DailyDozen/DailyDozen/.DS_Store b/DailyDozen/DailyDozen/.DS_Store new file mode 100644 index 00000000..f5bfb49b Binary files /dev/null and b/DailyDozen/DailyDozen/.DS_Store differ diff --git a/DailyDozen/DailyDozen/About/.DS_Store b/DailyDozen/DailyDozen/About/.DS_Store new file mode 100644 index 00000000..ff948ecf Binary files /dev/null and b/DailyDozen/DailyDozen/About/.DS_Store differ diff --git a/DailyDozen/DailyDozen/About/Controllers/AboutViewController.swift b/DailyDozen/DailyDozen/About/Controllers/AboutViewController.swift new file mode 100644 index 00000000..598ccca7 --- /dev/null +++ b/DailyDozen/DailyDozen/About/Controllers/AboutViewController.swift @@ -0,0 +1,98 @@ +// +// AboutViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 11.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import ActiveLabel + +// MARK: - Builder +class AboutBuilder { + + // MARK: - Nested + private struct Keys { + static let storyboard = "About" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Returns: The initial view controller in the storyboard. + static func instantiateController() -> UIViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() + else { fatalError("There should be a controller") } + viewController.title = "About this app" + + return viewController + } +} + +// MARK: - Controller +class AboutViewController: UITableViewController { + + // MARK: - Nested + private struct Regex { + static let book = "\\sHow Not to Die\\b" + static let site = "\\sNutritionFacts.org\\b" + static let christi = "\\sChristi Richards\\b" + static let const = "\\sKonstantin Khokhlov\\b" + static let elements = "\\sSketch Elements\\b" + } + + // MARK: - Outlets + @IBOutlet private weak var messageLabel: ActiveLabel! + @IBOutlet private weak var infoLabel: ActiveLabel! + @IBOutlet private weak var designLabel: ActiveLabel! + + // MARK: - UITableViewController + override func viewDidLoad() { + super.viewDidLoad() + + let bookType = ActiveType.custom(pattern: Regex.book) + messageLabel.enabledTypes.append(bookType) + + messageLabel.customize { label in + label.customColor[bookType] = UIColor.greenColor + label.handleCustomTap(for: bookType) { _ in + UIApplication.shared + .open(LinksService.shared.siteBook, + options: [:], + completionHandler: nil) + } + } + + provide(link: LinksService.shared.team, for: Regex.site, in: infoLabel) + provide(link: LinksService.shared.aboutChristi, for: Regex.christi, in: infoLabel) + provide(link: LinksService.shared.aboutConst, for: Regex.const, in: infoLabel) + provide(link: LinksService.shared.aboutElements, for: Regex.elements, in: designLabel) + } + + /// Provides a link for the current regex in the label. + /// + /// - Parameters: + /// - link: The link. + /// - pattern: The regex. + /// - label: The label. + private func provide(link: URL?, for pattern: String, in label: ActiveLabel) { + let about = ActiveType.custom(pattern: pattern) + label.enabledTypes.append(about) + + if let aboutLink = link { + label.customize { label in + label.customColor[about] = UIColor.greenColor + label.handleCustomTap(for: about) { _ in + UIApplication.shared + .open(aboutLink, + options: [:], + completionHandler: nil) + } + } + } + } +} diff --git a/DailyDozen/DailyDozen/About/Storyboards/About.storyboard b/DailyDozen/DailyDozen/About/Storyboards/About.storyboard new file mode 100644 index 00000000..c9584450 --- /dev/null +++ b/DailyDozen/DailyDozen/About/Storyboards/About.storyboard @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + HelveticaNeue + HelveticaNeue-Bold + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/App/.DS_Store b/DailyDozen/DailyDozen/App/.DS_Store new file mode 100644 index 00000000..1f013747 Binary files /dev/null and b/DailyDozen/DailyDozen/App/.DS_Store differ diff --git a/DailyDozen/DailyDozen/App/Controllers/AlertBuilder.swift b/DailyDozen/DailyDozen/App/Controllers/AlertBuilder.swift new file mode 100644 index 00000000..12b0c501 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Controllers/AlertBuilder.swift @@ -0,0 +1,74 @@ +// +// AlertBuilder.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 30.01.2018. +// Copyright © 2018 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class AlertBuilder { + + // MARK: - Nested + private struct Strings { + static let vitamins = "VITAMINS" + static let message = """ + Vitamin B12 and Vitamin D are essential for your health but do not count towards your daily servings. + + They are included in this app to provide you with an easy way to track your intake. + """ + static let confirm = "OK" + } + + private struct Keys { + static let title = "attributedTitle" + static let message = "attributedMessage" + static let textColor = "titleTextColor" + } + + enum AlertContent { + + case vitamin + + var title: String { + switch self { + case .vitamin: + return Strings.vitamins + } + } + + var message: String { + switch self { + case .vitamin: + return Strings.message + } + } + } + + static func instantiateController(for content: AlertContent) -> UIAlertController { + let alert = UIAlertController(title: content.title, message: content.title, preferredStyle: .actionSheet) + + alert.setValue( + NSAttributedString( + string: content.title, + attributes: [ + NSAttributedStringKey.font: UIFont.helveticaBold, + NSAttributedStringKey.foregroundColor: UIColor.greenColor]), + forKey: Keys.title) + + alert.setValue( + NSAttributedString( + string: content.message, + attributes: [ + NSAttributedStringKey.font: UIFont.helevetica, + NSAttributedStringKey.foregroundColor: UIColor.lightGray]), + forKey: Keys.message) + + let action = UIAlertAction(title: Strings.confirm, style: .cancel, handler: nil) + action.setValue(UIColor.greenColor, forKey: Keys.textColor) + alert.addAction(action) + + return alert + } +} diff --git a/DailyDozen/DailyDozen/App/Controllers/MainViewController.swift b/DailyDozen/DailyDozen/App/Controllers/MainViewController.swift new file mode 100644 index 00000000..ea7d8d6c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Controllers/MainViewController.swift @@ -0,0 +1,89 @@ +// +// MainViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 18.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import UserNotifications + +class MainViewController: UISplitViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + UNUserNotificationCenter.current().delegate = self + + UNUserNotificationCenter.current().removeAllPendingNotificationRequests() + + if UserDefaults.standard.object(forKey: "canNotificate") == nil { + + UNUserNotificationCenter.current().getNotificationSettings { settings in + UserDefaults.standard.set(settings.authorizationStatus == .authorized, forKey: "canNotificate") + } + } + + if UserDefaults.standard.object(forKey: "hour") == nil { + UserDefaults.standard.set(8, forKey: "hour") + } + + if UserDefaults.standard.object(forKey: "minute") == nil { + UserDefaults.standard.set(0, forKey: "minute") + } + + if UserDefaults.standard.object(forKey: "sound") == nil { + UserDefaults.standard.set(true, forKey: "sound") + } + + guard UserDefaults.standard.bool(forKey: "canNotificate") else { return } + + let content = UNMutableNotificationContent() + content.title = "DailyDozen app." + content.subtitle = "Do you remember about the app?" + content.body = "Use this app on a daily basis!" + content.badge = 1 + + if UserDefaults.standard.bool(forKey: "sound") { + content.sound = UNNotificationSound.default() + } + + var dateComponents = DateComponents() + dateComponents.hour = UserDefaults.standard.integer(forKey: "hour") + dateComponents.minute = UserDefaults.standard.integer(forKey: "minute") + + let dateTrigget = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) + + let request = UNNotificationRequest(identifier: "request", content: content, trigger: dateTrigget) + + UNUserNotificationCenter.current().add(request) { (error) in + if let error = error { + print(error.localizedDescription) + } + } + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return UIStatusBarStyle.lightContent + } +} + +extension MainViewController: UNUserNotificationCenterDelegate { + + func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + + completionHandler([.alert, .sound]) + } + + func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + + completionHandler() + } +} diff --git a/DailyDozen/DailyDozen/App/Extensions/Colors.swift b/DailyDozen/DailyDozen/App/Extensions/Colors.swift new file mode 100644 index 00000000..f685d67f --- /dev/null +++ b/DailyDozen/DailyDozen/App/Extensions/Colors.swift @@ -0,0 +1,36 @@ +// +// Colors.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 29.01.2018. +// Copyright © 2018 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +extension UIColor { + + static var greenColor: UIColor { + return UIColor(red: 127/255, green: 192/255, blue: 76/255, alpha: 1) + } + + static var lightGreenColor: UIColor { + return UIColor(red: 174/255, green: 215/255, blue: 142/255, alpha: 1) + } + + static var yellowColor: UIColor { + return UIColor(red: 235/255, green: 193/255, blue: 64/255, alpha: 1) + } + + static var darkRedColor: UIColor { + return UIColor(red: 198/255, green: 108/255, blue: 108/255, alpha: 1) + } + + static var lightGrayColor: UIColor { + return UIColor(red: 213/255, green: 213/255, blue: 213/255, alpha: 1) + } + + static var darkBlueColor: UIColor { + return UIColor(red: 60/255, green: 66/255, blue: 82/255, alpha: 1) + } +} diff --git a/DailyDozen/DailyDozen/App/Extensions/Date.swift b/DailyDozen/DailyDozen/App/Extensions/Date.swift new file mode 100644 index 00000000..1cc2b69a --- /dev/null +++ b/DailyDozen/DailyDozen/App/Extensions/Date.swift @@ -0,0 +1,108 @@ +// +// Date.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 09.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +extension Date { + + /// Returns a day name from the date. + var dayName: String { + let dateFormatter = DateFormatter() + dateFormatter.setLocalizedDateFormatFromTemplate("EEE") + return dateFormatter.string(from: self) + } + + /// Returns a day int for the date. + var day: Int { + return Calendar.current.component(.day, from: self) + } + + var monthName: String { + let dateFormatter = DateFormatter() + dateFormatter.setLocalizedDateFormatFromTemplate("MMM") + return dateFormatter.string(from: self) + } + + var month: Int { + return Calendar.current.component(.month, from: self) + } + + var year: Int { + return Calendar.current.component(.year, from: self) + } + + var hour: Int { + get { + return Calendar.current.component(.hour, from: self) + } + set { + let allowedRange = Calendar.current.range(of: .hour, in: .day, for: self)! + guard allowedRange.contains(newValue) else { return } + + let currentHour = Calendar.current.component(.hour, from: self) + let hoursToAdd = newValue - currentHour + if let date = Calendar.current.date(byAdding: .hour, value: hoursToAdd, to: self) { + self = date + } + } + } + + public var minute: Int { + get { + return Calendar.current.component(.minute, from: self) + } + set { + let allowedRange = Calendar.current.range(of: .minute, in: .hour, for: self)! + guard allowedRange.contains(newValue) else { return } + + let currentMinutes = Calendar.current.component(.minute, from: self) + let minutesToAdd = newValue - currentMinutes + if let date = Calendar.current.date(byAdding: .minute, value: minutesToAdd, to: self) { + self = date + } + } + } + + /// Returns a date string from the date. + /// + /// - Parameter style: A dateFormatter style (default is .medium). + /// - Returns: A date string. + func dateString(for style: DateFormatter.Style = .medium) -> String { + let dateFormatter = DateFormatter() + dateFormatter.timeStyle = .none + dateFormatter.dateStyle = style + dateFormatter.locale = Locale.current + return dateFormatter.string(from: self) + } + + /// Checks if a date is within the current date day. + /// + /// - Parameter date: The current date. + /// - Returns: Bool - Is a same day. + func isInCurrentDayWith(_ date: Date) -> Bool { + return Calendar.current.isDate(self, equalTo: date, toGranularity: .day) + } + + /// Checks if a date is within the current date month. + /// + /// - Parameter date: The current date. + /// - Returns: Bool - Is a same month. + func isInCurrentMonthWith(_ date: Date) -> Bool { + return Calendar.current.isDate(self, equalTo: date, toGranularity: .month) + } + + /// Returns a new date by adding the calendar component value. + /// + /// - Parameters: + /// - component: A component type. + /// - value: The calendar component value. + /// - Returns: A new date + func adding(_ component: Calendar.Component, value: Int) -> Date? { + return Calendar.current.date(byAdding: component, value: value, to: self) + } +} diff --git a/DailyDozen/DailyDozen/App/Extensions/Fonts.swift b/DailyDozen/DailyDozen/App/Extensions/Fonts.swift new file mode 100644 index 00000000..80262d5e --- /dev/null +++ b/DailyDozen/DailyDozen/App/Extensions/Fonts.swift @@ -0,0 +1,26 @@ +// +// Fonts.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 31.01.2018. +// Copyright © 2018 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +extension UIFont { + + // MARK: - Nested + private struct Keys { + static let helveticaBold = "Helvetica-Bold" + static let helvetica = "Helvetica" + } + + static var helevetica: UIFont { + return UIFont(name: Keys.helvetica, size: 17) ?? UIFont.systemFont(ofSize: 17) + } + + static var helveticaBold: UIFont { + return UIFont(name: Keys.helveticaBold, size: 22) ?? UIFont.boldSystemFont(ofSize: 22) + } +} diff --git a/DailyDozen/DailyDozen/App/Extensions/URL.swift b/DailyDozen/DailyDozen/App/Extensions/URL.swift new file mode 100644 index 00000000..e26a9700 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Extensions/URL.swift @@ -0,0 +1,21 @@ +// +// URL.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 24.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +extension URL { + + /// Returns a file URL in the documents directory. + /// + /// - Parameter file: A file name. + /// - Returns: A file URL in the documents directory. + static func inDocuments(for file: String) -> URL { + let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return path[0].appendingPathComponent(file) + } +} diff --git a/DailyDozen/DailyDozen/App/Realm/RealmConfig.swift b/DailyDozen/DailyDozen/App/Realm/RealmConfig.swift new file mode 100644 index 00000000..40fed3b4 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Realm/RealmConfig.swift @@ -0,0 +1,55 @@ +// +// RealmConfig.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 24.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation +import RealmSwift + +enum RealmConfig { + + private struct Keys { + static let realm = "main.realm" + } + + case servings + + /// A private instance of a Realm for the Servings. + private static let servingsConfig = Realm.Configuration( + fileURL: URL.inDocuments(for: Keys.realm), + objectTypes: [Doze.self, Item.self]) + + /// A public configuration instance of a Realm. + var configuration: Realm.Configuration { + switch self { + case .servings: + return RealmConfig.servingsConfig + } + } + + /// Returns an initial doze for the current date. + /// + /// - Parameter date: The current date. + static func initialDoze(for date: Date) -> Doze { + let items = [ + Item(name: "Beans", states: [false, false, false]), + Item(name: "Berries", states: [false]), + Item(name: "Other Fruits", states: [false, false, false]), + Item(name: "Cruciferous Vegetables", states: [false]), + Item(name: "Greens", states: [false, false]), + Item(name: "Other Vegetables", states: [false, false]), + Item(name: "Flaxseeds", states: [false]), + Item(name: "Nuts", states: [false]), + Item(name: "Spices", states: [false]), + Item(name: "Whole Grains", states: [false, false, false]), + Item(name: "Beverages", states: [false, false, false, false, false]), + Item(name: "Exercise", states: [false]), + Item(name: "Vitamin B12", states: [false]), + Item(name: "Vitamin D", states: [false]) + ] + return Doze(date: date, items: items) + } +} diff --git a/DailyDozen/DailyDozen/App/Realm/RealmProvider.swift b/DailyDozen/DailyDozen/App/Realm/RealmProvider.swift new file mode 100644 index 00000000..1bd21962 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Realm/RealmProvider.swift @@ -0,0 +1,84 @@ +// +// RealmProvider.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 30.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import RealmSwift + +protocol RealmDelegate: AnyObject { + func didUpdateFile() +} + +class RealmProvider { + + private let realm: Realm + private var unsavedDoze: Doze? + + init() { + guard let realm = try? Realm(configuration: RealmConfig.servings.configuration) else { + fatalError("There should be a realm") + } + self.realm = realm + } + + /// Returns a Doze object for the current date. + /// + /// - Returns: The doze. + func getDoze(for date: Date) -> Doze { + let doze = realm + .objects(Doze.self) + .filter { $0.date.isInCurrentDayWith(date) } + .first ?? RealmConfig.initialDoze(for: date) + unsavedDoze = doze + return doze + } + + func getDozes() -> Results { + return realm.objects(Doze.self) + } + + /// Updates an Item object with an ID for new states. + /// + /// - Parameters: + /// - states: The new state. + /// - id: The ID. + func saveStates(_ states: [Bool], with id: String) { + saveDoze() + do { + try realm.write { + realm.create(Item.self, value: ["id": id, "states": states], update: true) + } + } catch { + print(error.localizedDescription) + } + } + + func updateStreak(_ streak: Int, with id: String) { + saveDoze() + do { + try realm.write { + realm.create(Item.self, value: ["id": id, "streak": streak], update: true) + } + } catch { + print(error.localizedDescription) + } + } + + /// Saves the unsaved doze. + private func saveDoze() { + if let doze = unsavedDoze { + do { + try realm.write { + realm.add(doze, update: true) + unsavedDoze = nil + } + } catch { + print(error.localizedDescription) + } + } + } +} diff --git a/DailyDozen/DailyDozen/App/Resources/.DS_Store b/DailyDozen/DailyDozen/App/Resources/.DS_Store new file mode 100644 index 00000000..7351136e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/.DS_Store differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/.DS_Store b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/.DS_Store new file mode 100644 index 00000000..2285a546 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/.DS_Store differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..dc56ea3c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,186 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "ItunesArtwork@2x.png", + "scale" : "1x" + }, + { + "size" : "24x24", + "idiom" : "watch", + "filename" : "Icon-24@2x.png", + "scale" : "2x", + "role" : "notificationCenter", + "subtype" : "38mm" + }, + { + "size" : "27.5x27.5", + "idiom" : "watch", + "filename" : "Icon-27.5@2x.png", + "scale" : "2x", + "role" : "notificationCenter", + "subtype" : "42mm" + }, + { + "size" : "29x29", + "idiom" : "watch", + "filename" : "Icon-29@2x.png", + "role" : "companionSettings", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "watch", + "filename" : "Icon-29@3x.png", + "role" : "companionSettings", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "watch", + "filename" : "Icon-40@2x.png", + "scale" : "2x", + "role" : "appLauncher", + "subtype" : "38mm" + }, + { + "size" : "44x44", + "idiom" : "watch", + "filename" : "Icon-44@2x.png", + "scale" : "2x", + "role" : "longLook", + "subtype" : "42mm" + }, + { + "size" : "86x86", + "idiom" : "watch", + "filename" : "Icon-86@2x.png", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "38mm" + }, + { + "size" : "98x98", + "idiom" : "watch", + "filename" : "Icon-98@2x.png", + "scale" : "2x", + "role" : "quickLook", + "subtype" : "42mm" + }, + { + "idiom" : "watch-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "pre-rendered" : true + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-24@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-24@2x.png new file mode 100644 index 00000000..04a04951 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-24@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-27.5@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-27.5@2x.png new file mode 100644 index 00000000..a073a01e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-27.5@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png new file mode 100644 index 00000000..f6873c78 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png new file mode 100644 index 00000000..265d02e4 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 00000000..99aaf04d Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-44@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-44@2x.png new file mode 100644 index 00000000..f7bcc51f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-44@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-86@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-86@2x.png new file mode 100644 index 00000000..4810d895 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-86@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-98@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-98@2x.png new file mode 100644 index 00000000..e871ccbf Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-98@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..9d529031 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png new file mode 100644 index 00000000..13f19ac3 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..13f19ac3 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..e1420fc4 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..b5e3db08 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png new file mode 100644 index 00000000..f6873c78 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..f6873c78 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..265d02e4 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..13f19ac3 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png new file mode 100644 index 00000000..99aaf04d Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..99aaf04d Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..4f5cf6d7 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..4f5cf6d7 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..dbf136a7 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..7e3e6fbb Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..4f5eb25a Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..2cb52945 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 00000000..3ed426b8 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/.DS_Store b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/.DS_Store new file mode 100644 index 00000000..53d68796 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/.DS_Store differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/Contents.json new file mode 100644 index 00000000..e1b93172 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "beans.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/beans.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/beans.png new file mode 100644 index 00000000..5a78b0aa Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beans.imageset/beans.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/Contents.json new file mode 100644 index 00000000..5b7c314b --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "berries.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/berries.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/berries.png new file mode 100644 index 00000000..1f5e3240 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/berries.imageset/berries.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/Contents.json new file mode 100644 index 00000000..3c3f31df --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "beverages.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/beverages.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/beverages.png new file mode 100644 index 00000000..b9d2963b Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/beverages.imageset/beverages.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/Contents.json new file mode 100644 index 00000000..9052b21f --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "cruciferous_vegetables.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/cruciferous_vegetables.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/cruciferous_vegetables.png new file mode 100644 index 00000000..9bbcad30 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/cruciferous_vegetables.imageset/cruciferous_vegetables.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/Contents.json new file mode 100644 index 00000000..8bb073b4 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "exercise.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/exercise.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/exercise.png new file mode 100644 index 00000000..effef11d Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/exercise.imageset/exercise.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/Contents.json new file mode 100644 index 00000000..a26353ee --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "flaxseeds.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/flaxseeds.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/flaxseeds.png new file mode 100644 index 00000000..9179c59e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/flaxseeds.imageset/flaxseeds.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/Contents.json new file mode 100644 index 00000000..795d5732 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "greens.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/greens.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/greens.png new file mode 100644 index 00000000..157e1414 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/greens.imageset/greens.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/Contents.json new file mode 100644 index 00000000..da1f6a18 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "nuts.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/nuts.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/nuts.png new file mode 100644 index 00000000..e812eeeb Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/nuts.imageset/nuts.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/Contents.json new file mode 100644 index 00000000..d2ca71a3 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "other_fruits.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/other_fruits.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/other_fruits.png new file mode 100644 index 00000000..60e51cc2 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_fruits.imageset/other_fruits.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/Contents.json new file mode 100644 index 00000000..cdefbdd3 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "other_vegetables.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/other_vegetables.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/other_vegetables.png new file mode 100644 index 00000000..c774ad53 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/other_vegetables.imageset/other_vegetables.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/Contents.json new file mode 100644 index 00000000..c5692340 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "spices.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/spices.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/spices.png new file mode 100644 index 00000000..7aced7ce Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/spices.imageset/spices.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/Contents.json new file mode 100644 index 00000000..bf25c511 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "whole_grains.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/whole_grains.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/whole_grains.png new file mode 100644 index 00000000..8cc95fca Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Details/whole_grains.imageset/whole_grains.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/Contents.json new file mode 100644 index 00000000..b55ad659 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_calendar.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_calendar@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_calendar@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar.png new file mode 100644 index 00000000..6e2e63b3 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@2x.png new file mode 100644 index 00000000..f04ae65d Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@3x.png new file mode 100644 index 00000000..63a5494c Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_calendar.imageset/ic_calendar@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/Contents.json new file mode 100644 index 00000000..810994fb --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_left.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_left@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_left@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left.png new file mode 100644 index 00000000..f08b8985 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@2x.png new file mode 100644 index 00000000..9d971f59 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@3x.png new file mode 100644 index 00000000..6b8188e1 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left.imageset/ic_left@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/Contents.json new file mode 100644 index 00000000..53976a61 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_left_double.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_left_double@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_left_double@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double.png new file mode 100644 index 00000000..693bffbd Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@2x.png new file mode 100644 index 00000000..12893ed1 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@3x.png new file mode 100644 index 00000000..0ea2241f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_left_double.imageset/ic_left_double@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/Contents.json new file mode 100644 index 00000000..d4915996 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_menu.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_menu@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu.png new file mode 100644 index 00000000..05f09fdc Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu@2x.png new file mode 100644 index 00000000..82b705f2 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_menu.imageset/ic_menu@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/Contents.json new file mode 100644 index 00000000..7f084459 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_right.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_right@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_right@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right.png new file mode 100644 index 00000000..8ef818a0 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@2x.png new file mode 100644 index 00000000..8e79b2ea Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@3x.png new file mode 100644 index 00000000..71fb55aa Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right.imageset/ic_right@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/Contents.json new file mode 100644 index 00000000..21aa0e38 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_right_double.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_right_double@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_right_double@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double.png new file mode 100644 index 00000000..e60565e7 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@2x.png new file mode 100644 index 00000000..ee140e60 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@3x.png new file mode 100644 index 00000000..1369bfda Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_right_double.imageset/ic_right_double@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/Contents.json new file mode 100644 index 00000000..e2ee5ce2 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_star.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_star@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_star@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star.png new file mode 100644 index 00000000..4b1b997e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@2x.png new file mode 100644 index 00000000..be2faa1b Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@3x.png new file mode 100644 index 00000000..72189aee Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_star.imageset/ic_star@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/Contents.json new file mode 100644 index 00000000..a7ab428b --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_stat.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_stat@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_stat@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat.png new file mode 100644 index 00000000..5e81a55f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@2x.png new file mode 100644 index 00000000..413a46be Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@3x.png new file mode 100644 index 00000000..7989f07f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/ic_stat.imageset/ic_stat@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/Contents.json new file mode 100644 index 00000000..9b404d1a --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "music_info.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "music_info@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "music_info@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info.png new file mode 100644 index 00000000..96f7580e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@2x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@2x.png new file mode 100644 index 00000000..aef2157b Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@2x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@3x.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@3x.png new file mode 100644 index 00000000..225bf364 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Icons/music_info.imageset/music_info@3x.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/.DS_Store b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/.DS_Store new file mode 100644 index 00000000..ea97f4fb Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/.DS_Store differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/Contents.json new file mode 100644 index 00000000..ebb8fe9f --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "dr_greger.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/dr_greger.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/dr_greger.png new file mode 100644 index 00000000..f3719cc3 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/dr_greger.imageset/dr_greger.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/Contents.json new file mode 100644 index 00000000..8463acaf --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "logo.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/logo.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/logo.png new file mode 100644 index 00000000..da63967f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Images/logo.imageset/logo.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/Contents.json new file mode 100644 index 00000000..06a5b274 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_beans.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/ic_beans.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/ic_beans.png new file mode 100644 index 00000000..3a74be28 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beans.imageset/ic_beans.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/Contents.json new file mode 100644 index 00000000..202d64da --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_berries.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/ic_berries.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/ic_berries.png new file mode 100644 index 00000000..aa361b0f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_berries.imageset/ic_berries.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/Contents.json new file mode 100644 index 00000000..d2b64a67 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_beverages.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/ic_beverages.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/ic_beverages.png new file mode 100644 index 00000000..a9718ad5 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_beverages.imageset/ic_beverages.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/Contents.json new file mode 100644 index 00000000..68624a89 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_cruciferous_vegetables.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/ic_cruciferous_vegetables.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/ic_cruciferous_vegetables.png new file mode 100644 index 00000000..693c46a6 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_cruciferous_vegetables.imageset/ic_cruciferous_vegetables.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/Contents.json new file mode 100644 index 00000000..74266589 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_exercise.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/ic_exercise.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/ic_exercise.png new file mode 100644 index 00000000..0e3218be Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_exercise.imageset/ic_exercise.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/Contents.json new file mode 100644 index 00000000..4e521e1c --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_flaxseeds.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/ic_flaxseeds.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/ic_flaxseeds.png new file mode 100644 index 00000000..9baca10a Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_flaxseeds.imageset/ic_flaxseeds.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/Contents.json new file mode 100644 index 00000000..be17ff25 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_greens.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/ic_greens.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/ic_greens.png new file mode 100644 index 00000000..35a1d684 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_greens.imageset/ic_greens.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/Contents.json new file mode 100644 index 00000000..06f632c6 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_nuts.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/ic_nuts.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/ic_nuts.png new file mode 100644 index 00000000..fb9b9b4f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_nuts.imageset/ic_nuts.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/Contents.json new file mode 100644 index 00000000..1f5ff39b --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_other_fruits.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/ic_other_fruits.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/ic_other_fruits.png new file mode 100644 index 00000000..57fd30ad Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_fruits.imageset/ic_other_fruits.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/Contents.json new file mode 100644 index 00000000..a0fc163b --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_other_vegetables.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/ic_other_vegetables.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/ic_other_vegetables.png new file mode 100644 index 00000000..1c59c58e Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_other_vegetables.imageset/ic_other_vegetables.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/Contents.json new file mode 100644 index 00000000..14648ddb --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_settings_black_24dp.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/ic_settings_black_24dp.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/ic_settings_black_24dp.png new file mode 100644 index 00000000..476d5c97 Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_settings_black_24dp.imageset/ic_settings_black_24dp.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/Contents.json new file mode 100644 index 00000000..d5014c26 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_spices.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/ic_spices.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/ic_spices.png new file mode 100644 index 00000000..1497963a Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_spices.imageset/ic_spices.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/Contents.json new file mode 100644 index 00000000..dae7a59e --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_vitamin_b12.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/ic_vitamin_b12.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/ic_vitamin_b12.png new file mode 100644 index 00000000..64c7ecea Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_b12.imageset/ic_vitamin_b12.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/Contents.json new file mode 100644 index 00000000..048ae13d --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_vitamin_d.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/ic_vitamin_d.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/ic_vitamin_d.png new file mode 100644 index 00000000..b8f463da Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_vitamin_d.imageset/ic_vitamin_d.png differ diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/Contents.json b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/Contents.json new file mode 100644 index 00000000..416839fd --- /dev/null +++ b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_whole_grains.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/ic_whole_grains.png b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/ic_whole_grains.png new file mode 100644 index 00000000..68159e8f Binary files /dev/null and b/DailyDozen/DailyDozen/App/Resources/Assets.xcassets/Servings/ic_whole_grains.imageset/ic_whole_grains.png differ diff --git a/DailyDozen/DailyDozen/App/Services/LinkService.swift b/DailyDozen/DailyDozen/App/Services/LinkService.swift new file mode 100644 index 00000000..2c303990 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Services/LinkService.swift @@ -0,0 +1,102 @@ +// +// LinkService.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 13.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +class LinksService { + + // MARK: - Nested + private struct Keys { + static let baseURL = "base_url" + } + + private struct URLKeys { + static let topics = "topics" + static let book = "book" + static let team = "team" + } + + private struct About { + static let christi = "https://github.com/christirichards" + static let const = "https://github.com/justaninja" + static let elements = "https://sketchapp.com/elements" + } + + // MARK: - Properties + private let baseURL: URL + + var siteMain: URL { + return baseURL + } + + var siteBook: URL { + return baseURL + .appendingPathComponent(URLKeys.book) + } + + var team: URL { + return baseURL + .appendingPathComponent(URLKeys.team) + } + + var aboutChristi: URL? { + return URL(string: About.christi) + } + + var aboutConst: URL? { + return URL(string: About.const) + } + + var aboutElements: URL? { + return URL(string: About.elements) + } + + /// Returns the shared LinksService object. + static let shared: LinksService = { + guard let path = Bundle.main.path( + forResource: "LinkSettings", + ofType: "plist") else { + fatalError("There should be a settings file") + } + + guard let dictionary = NSDictionary(contentsOfFile: path) else { + fatalError("There should be a settings dictionary") + } + + guard let urlString = dictionary[Keys.baseURL] as? String, + let url = URL(string: urlString) else { + fatalError("There should be a base URL") + } + + return LinksService(url: url) + }() + + // MARK: - Inits + private init(url: URL) { + baseURL = url + } + + /// Returns a url for the current topic. + /// + /// - Parameter topic: The current topic. + /// - Returns: A url. + func link(forTopic topic: String) -> URL { + return baseURL + .appendingPathComponent(URLKeys.topics) + .appendingPathComponent(topic) + } + + /// Returns a url for the current menu item. + /// + /// - Parameter menu: The current menu item. + /// - Returns: A url. + func link(forMenu menu: String) -> URL { + return baseURL + .appendingPathComponent(menu) + } +} diff --git a/DailyDozen/DailyDozen/App/Services/LinkSettings.plist b/DailyDozen/DailyDozen/App/Services/LinkSettings.plist new file mode 100644 index 00000000..d9674bed --- /dev/null +++ b/DailyDozen/DailyDozen/App/Services/LinkSettings.plist @@ -0,0 +1,8 @@ + + + + + base_url + https://nutritionfacts.org/ + + diff --git a/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/LaunchScreen.storyboard b/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..083a8bb4 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/Main.storyboard b/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/Main.storyboard new file mode 100644 index 00000000..e621a85a --- /dev/null +++ b/DailyDozen/DailyDozen/App/Storyboards/Base.lproj/Main.storyboard @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/App/SupportingFiles/AppDelegate.swift b/DailyDozen/DailyDozen/App/SupportingFiles/AppDelegate.swift new file mode 100644 index 00000000..22d29c74 --- /dev/null +++ b/DailyDozen/DailyDozen/App/SupportingFiles/AppDelegate.swift @@ -0,0 +1,62 @@ +// +// AppDelegate.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 18.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import UserNotifications + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + // MARK: - Nested + private struct Keys { + static let extens = "realm" + static let main = "main.realm" + static let title = "Restore Backup" + static let message = "Any existing data will be deleted before restoring from backup. Do you wish to continue?" + static let confirm = "OK" + static let decline = "NO" + } + + weak var realmDelegate: RealmDelegate? + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (_, error) in + if let error = error { + print(error.localizedDescription) + } + } + return true + } + + func applicationDidBecomeActive(_ application: UIApplication) { + application.applicationIconBadgeNumber = 0 + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { + guard url.pathExtension == Keys.extens else { return false } + + let importAlert = UIAlertController(title: Keys.title, + message: Keys.message, + preferredStyle: .alert) + let confirm = UIAlertAction(title: Keys.confirm, style: .default) { [weak self] (_) in + try? FileManager.default.removeItem(at: URL.inDocuments(for: Keys.main)) + try? FileManager.default.copyItem(at: url, to: URL.inDocuments(for: Keys.main)) + self?.realmDelegate?.didUpdateFile() + } + importAlert.addAction(confirm) + + let decline = UIAlertAction(title: Keys.decline, style: .cancel, handler: nil) + importAlert.addAction(decline) + + window?.rootViewController?.show(importAlert, sender: nil) + return true + } +} diff --git a/DailyDozen/DailyDozen/App/SupportingFiles/Info.plist b/DailyDozen/DailyDozen/App/SupportingFiles/Info.plist new file mode 100644 index 00000000..ff25061d --- /dev/null +++ b/DailyDozen/DailyDozen/App/SupportingFiles/Info.plist @@ -0,0 +1,84 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDocumentTypes + + + CFBundleTypeName + DailyDozen + CFBundleTypeRole + Editor + LSHandlerRank + Owner + LSItemContentTypes + + org.nutritionfacts.DailyDozen.realm + + + + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 2.6 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UIStatusBarHidden + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + DailyDozen + UTTypeIdentifier + org.nutritionfacts.DailyDozen.realm + UTTypeTagSpecification + + public.filename-extension + realm + public.mime-type + application/DailyDozen + + + + + diff --git a/DailyDozen/DailyDozen/App/Texts/Details.plist b/DailyDozen/DailyDozen/App/Texts/Details.plist new file mode 100644 index 00000000..3a122835 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Texts/Details.plist @@ -0,0 +1,1206 @@ + + + + + Beans + + Sizes + + Metric + + 60 g of hummus or bean dip + 130 g cooked beans, split peas, lentils, tofu, or tempeh + 150 g of fresh peas or sprouted lentils + + Imperial + + ¼ cup of hummus or bean dip + ½ cup cooked beans, split peas, lentils, tofu, or tempeh + 1 cup of fresh peas or sprouted lentils + + + Types + + + Black beans + black-beans + + + Black-eyed peas + + + + Butter beans + + + + Cannellini beans + + + + Chickpeas + chickpeas + + + Edamame + edamame + + + English peas + + + + Garbanzo beans + garbanzo-beans + + + Great northern beans + + + + Kidney beans + kidney-beans + + + Lentils (beluga, french, and red varieties) + lentils + + + Miso + miso + + + Navy beans + navy-beans + + + Pinto beans + pinto-beans + + + Small red beans + + + + Split peas (yellow or green) + split-peas + + + Tempeh + tempeh + + + Topic + beans + + Berries + + Sizes + + Metric + + 60 g fresh or frozen + 40 g dried + + Imperial + + ½ cup fresh or frozen + ¼ cup dried + + + Types + + + Acai berries + acai-berries + + + Barberries + barberries + + + Blackberries + blackberries + + + Blueberries + blueberries + + + Cherries (sweet or tart) + cherries + + + Concord grapes + concord-grapes + + + Cranberries + cranberries + + + Goji berries + goji-berries + + + Kumquats + + + + Mulberries + + + + Raspberries (black or red) + raspberries + + + Strawberries + strawberries + + + Topic + berries + + Other Fruits + + Sizes + + Metric + + 1 medium-sized fruit + 120 g cut-up fruit + 40 g dried fruit + + Imperial + + 1 medium-sized fruit + 1 cup cut-up fruit + ¼ cup dried fruit + + + Types + + + Apples + apples + + + Dried apricots + apricots + + + Avocados + avocados + + + Bananas + bananas + + + Cantaloupe + cantaloupe + + + Clementines + + + + Dates + dates + + + Dried figs + figs + + + Grapefruit + grapefruit + + + Honeydew + + + + Kiwifruit + kiwifruit + + + Lemons + lemons + + + Limes + limes + + + Lychees + + + + Mangos + mango + + + Nectarines + + + + Oranges + oranges + + + Papaya + papaya + + + Passion fruit + + + + Peaches + peaches + + + Pears + pears + + + Pineapple + pineapples + + + Plums (especially black plums) + plums + + + Pluots + + + + Pomegranates + pomegranates + + + Prunes + prunes + + + Tangerines + + + + Watermelon + watermelon + + + Topic + fruit + + Cruciferous Vegetables + + Sizes + + Metric + + 30–80 g chopped + 12 g Brussels or broccoli sprouts + 1 tablespoon horseradish + + Imperial + + ½ cup chopped + ¼ cup Brussels or broccoli sprouts + 1 tablespoon horseradish + + + Types + + + Arugula + + + + Bok choy + bok-choy + + + Broccoli + broccoli + + + Brussels sprouts + brussels-sprouts + + + Cabbage + cabbage + + + Cauliflower + cauliflower + + + Collard greens + collard-greens + + + Horseradish + + + + Kale (black, green, and red) + kale + + + Mustard greens + mustard-greens + + + Radishes + radishes + + + Turnip greens + turnips + + + Watercress + watercress + + + Topic + cruciferous-vegetables + + Greens + + Sizes + + Metric + + 60 g raw + 90 g cooked + + Imperial + + 1 cup raw + ½ cup cooked + + + Types + + + Arugula + + + + Beet greens + beet-greens + + + Collard greens + collard-greens + + + Kale (black, green, and red) + kale + + + Mesclun mix (assorted young salad greens) + mesclun-mix + + + Mustard greens + mustard-greens + + + Sorrel + + + + Spinach + spinach + + + Swiss chard + swiss-chard + + + Turnip greens + turnips + + + Topic + greens + + Other Vegetables + + Sizes + + Metric + + 60 g raw leafy vegetables + 50 g raw or cooked nonleafy vegetables + 125 ml vegetable juice + 7 g + + Imperial + + 1 cup raw leafy vegetables + ½ cup raw or cooked nonleafy vegetables + ½ cup vegetable juice + ¼ cup dried mushrooms + + + Types + + + Artichokes + artichokes + + + Asparagus + asparagus + + + Beets + beets + + + Bell peppers + bell-peppers + + + Carrots + carrots + + + Corn + corn + + + Garlic + garlic + + + Mushrooms (button, oyster, portobello, and shiitake) + mushrooms + + + Okra + okra + + + Onions + onions + + + Pumpkin + pumpkin + + + Purple potatoes + purple-potatoes + + + Sea vegetables (arame, dulse, and nori) + sea-vegetables + + + Snap peas + + + + Squash (delicata, summer, and spaghetti squash varieties) + squash + + + Sweet potatoes/yams + sweet-potatoes + + + Tomatoes + tomatoes + + + Zucchini + zucchini + + + Topic + vegetables + + Flaxseeds + + Sizes + + Metric + + 1 tablespoon ground + + Imperial + + 1 tablespoon ground + + + Types + + + Brown flaxseeds + + + + Golden flaxseeds + + + + Topic + flax-seeds + + Nuts + + Sizes + + Metric + + 30 g nuts or seeds + 2 tablespoons nut or seed butter + + Imperial + + ¼ cup nuts or seeds + 2 tablespoons nut or seed butter + + + Types + + + Almonds + almonds + + + Brazil nuts + brazil-nuts + + + Cashews + cashews + + + Chia seeds + chia-seeds + + + Hazelnuts + hazelnuts + + + Hemp seeds + + + + Macadamia nuts + macadamia-nuts + + + Pecans + pecans + + + Pistachios + pistachios + + + Pumpkin seeds + pumpkin-seeds + + + Sesame seeds + sesame-seeds + + + Sunflower seeds + sunflower-seeds + + + Walnuts + walnuts + + + Topic + nuts + + Spices + + Sizes + + Metric + + ¼ teaspoon of turmeric + Any other (salt-free) herbs and spices you enjoy + + Imperial + + ¼ teaspoon of turmeric + Any other (salt-free) herbs and spices you enjoy + + + Types + + + Allspice + allspice + + + Barberries + barberries + + + Basil + basil + + + Bay leaves + + + + Cardamom + cardamom + + + Chili powder + + + + Cilantro + cilantro + + + Cinnamon + cinnamon + + + Cloves + cloves + + + Coriander + coriander + + + Cumin + cumin + + + Curry powder + curry-powder + + + Dill + + + + Fenugreek + fenugreek + + + Garlic + garlic + + + Ginger + ginger + + + Horseradish + + + + Lemongrass + lemongrass + + + Marjoram + marjoram + + + Mustard powder + mustard-powder + + + Nutmeg + nutmeg + + + Oregano + oregano + + + Parsley + parsley + + + Pepper + pepper + + + Peppermint + peppermint + + + Rosemary + rosemary + + + Saffron + saffron + + + Sage + + + + Smoked paprika + + + + Thyme + thyme + + + Turmeric + turmeric + + + Vanilla + + + + Topic + spices + + Whole Grains + + Sizes + + Metric + + 100 g hot cereal or cooked grains, pasta, or corn kernels + 50 g cold cereal + 1 tortilla or slice of bread + ½ a bagel or English muffin + 30 g popped popcorn + + Imperial + + ½ cup hot cereal or cooked grains, pasta, or corn kernels + 1 cup cold cereal + 1 tortilla or slice of bread + ½ a bagel or English muffin + 3 cups popped popcorn + + + Types + + + Barley + barley + + + Brown rice + rice + + + Buckwheat + buckwheat + + + Millet + millet + + + Oats + oats + + + Whole-wheat pasta + pasta + + + Popcorn + popcorn + + + Quinoa + quinoa + + + Rye + rye + + + Teff + teff + + + Wild rice + rice + + + Topic + grains + + Beverages + + Sizes + + Metric + + One glass (350 ml) + + Imperial + + One glass (12 ounces) + + + Types + + + Black tea + black-tea + + + Chai tea + chai-tea + + + Vanilla chamomile tea + chamomile-tea + + + Coffee + coffee + + + Earl grey tea + earl-grey-tea + + + Green tea + green-tea + + + Hibiscus tea + hibiscus-tea + + + Hot chocolate + + + + Jasmine tea + jasmine-tea + + + Lemon balm tea + + + + Matcha tea + matcha + + + Almond blossom oolong tea + oolong-tea + + + Peppermint tea + + + + Rooibos tea + rooibos-tea + + + Water + water + + + White tea + white-tea + + + Topic + beverages + + Exercise + + Sizes + + Metric + + 90 minutes of moderate-intensity activity + 40 minutes of vigorous-intensity activity + + Imperial + + 90 minutes of moderate-intensity activity + 40 minutes of vigorous-intensity activity + + + Types + + + Bicycling + + + + Canoeing + + + + Dancing + + + + Dodgeball + + + + Downhill skiing + + + + Fencing + + + + Hiking + + + + Housework + + + + Ice-skating + + + + Inline skating + + + + Juggling + + + + Jumping on a trampoline + + + + Paddle boating + + + + Playing frisbee + + + + Roller-skating + + + + Shooting baskets + + + + Shoveling light snow + + + + Skateboarding + + + + Snorkeling + + + + Surfing + + + + Swimming recreationally + + + + Tennis (doubles) + + + + Treading water + + + + Walking briskly (4 mph) + + + + Water aerobics + + + + Waterskiing + + + + Yard work + + + + Yoga + + + + Backpacking + + + + Basketball + + + + Bicycling uphill + + + + Circuit weight training + + + + Cross-country skiing + + + + Football + + + + Hockey + + + + Jogging + + + + Jumping jacks + + + + Jumping rope + + + + Lacrosse + + + + Push-ups + + + + Pull-ups + + + + Racquetball + + + + Rock climbing + + + + Rugby + + + + Running + + + + Scuba diving + + + + Tennis (singles) + + + + Soccer + + + + Speed skating + + + + Squash + + + + Step aerobics + + + + Swimming laps + + + + Walking briskly uphill + + + + Water jogging + + + + Topic + exercise + + Vitamin B12 + + Sizes + + Metric + + Imperial + + + Types + + + + Topic + vitamin-b12 + + Vitamin D + + Sizes + + Metric + + Imperial + + + Types + + + + Topic + vitamin-d-supplements + + + diff --git a/DailyDozen/DailyDozen/App/Texts/TextsProvider.swift b/DailyDozen/DailyDozen/App/Texts/TextsProvider.swift new file mode 100644 index 00000000..4f914732 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Texts/TextsProvider.swift @@ -0,0 +1,85 @@ +// +// TextsProvider.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +class TextsProvider { + + // MARK: - Nested + private struct Keys { + static let details = "Details" + static let sizes = "Sizes" + static let metric = "Metric" + static let imperial = "Imperial" + static let types = "Types" + static let topic = "Topic" + static let plist = "plist" + } + + /// Returns the shared TextsProvider object. + static let shared: TextsProvider = { + guard + let path = Bundle.main.path( + forResource: Keys.details, ofType: Keys.plist) + else { fatalError("There should be a settings file") } + + guard + let dictionary = NSDictionary(contentsOfFile: path) as? [String : Any] + else { fatalError("There should be a dictionary") } + + return TextsProvider(dictionary: dictionary) + }() + + private let dictionary: [String : Any] + + init(dictionary: [String : Any]) { + self.dictionary = dictionary + } + + /// Loads static texts for the current item. + /// + /// - Parameter itemName: The current item name. + /// - Returns: A detail view model for static texts. + func loadDetail(for itemName: String) -> DetailViewModel { + guard + let item = dictionary[itemName] as? [String: Any] + else { fatalError("There should be an item") } + + guard + let sizes = item[Keys.sizes] as? [String: Any], + let metric = sizes[Keys.metric] as? [String], + let imperial = sizes[Keys.imperial] as? [String] + else { fatalError("There should be sizes") } + + guard + let types = item[Keys.types] as? [[String: String]] + else { fatalError("There should be types") } + + guard + let topic = item[Keys.topic] as? String + else { fatalError("There should be a topic") } + + return DetailViewModel(itemName: itemName, topic: topic, metricSizes: metric, imperialSizes: imperial, types: types) + } + + /// Returns the topic for the current item name. + /// + /// - Parameter itemName: The current item name. + /// - Returns: The topic. + func getTopic(for itemName: String) -> String { + guard + let item = dictionary[itemName] as? [String: Any] + else { fatalError("There should be an item") } + + guard + let topic = item[Keys.topic] as? String + else { fatalError("There should be a topic") } + + return topic + } +} diff --git a/DailyDozen/DailyDozen/App/Views/RoundedButton.swift b/DailyDozen/DailyDozen/App/Views/RoundedButton.swift new file mode 100644 index 00000000..116c6d0a --- /dev/null +++ b/DailyDozen/DailyDozen/App/Views/RoundedButton.swift @@ -0,0 +1,68 @@ +// +// RoundedButton.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 28.11.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +//@IBDesignable +class RoundedButton: UIButton { + + @IBInspectable + var cornerRadius: CGFloat = 5 { + didSet { + layer.cornerRadius = cornerRadius + } + } + + @IBInspectable + var borderWidth: CGFloat = 1 { + didSet { + layer.borderWidth = borderWidth + } + } + + @IBInspectable + var borderColor: UIColor = UIColor.clear { + didSet { + layer.borderColor = borderColor.cgColor + } + } + + @IBInspectable + var shadowColor: UIColor = UIColor.clear { + didSet { + layer.shadowColor = shadowColor.cgColor + } + } + + @IBInspectable + var shadowRadius: CGFloat = 2 { + didSet { + layer.shadowRadius = 2 + } + } + + @IBInspectable + var shadowOffset: CGSize = CGSize.zero { + didSet { + layer.shadowOffset = shadowOffset + } + } + + @IBInspectable + var shadowOpacity: Float = 1 { + didSet { + layer.shadowOpacity = shadowOpacity + } + } + + override var isEnabled: Bool { + didSet { + layer.borderColor = isEnabled ? borderColor.cgColor : borderColor.withAlphaComponent(0.5).cgColor + } + } +} diff --git a/DailyDozen/DailyDozen/App/Views/RoundedView.swift b/DailyDozen/DailyDozen/App/Views/RoundedView.swift new file mode 100644 index 00000000..7197f494 --- /dev/null +++ b/DailyDozen/DailyDozen/App/Views/RoundedView.swift @@ -0,0 +1,62 @@ +// +// RoundedView.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 11.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +//@IBDesignable +class RoundedView: UIView { + + @IBInspectable + var cornerRadius: CGFloat = 5 { + didSet { + layer.cornerRadius = cornerRadius + } + } + + @IBInspectable + var borderWidth: CGFloat = 1 { + didSet { + layer.borderWidth = borderWidth + } + } + + @IBInspectable + var borderColor: UIColor = UIColor.clear { + didSet { + layer.borderColor = borderColor.cgColor + } + } + + @IBInspectable + var shadowColor: UIColor = UIColor.clear { + didSet { + layer.shadowColor = shadowColor.cgColor + } + } + + @IBInspectable + var shadowRadius: CGFloat = 2 { + didSet { + layer.shadowRadius = 2 + } + } + + @IBInspectable + var shadowOffset: CGSize = CGSize.zero { + didSet { + layer.shadowOffset = shadowOffset + } + } + + @IBInspectable + var shadowOpacity: Float = 1 { + didSet { + layer.shadowOpacity = shadowOpacity + } + } +} diff --git a/DailyDozen/DailyDozen/Details/.DS_Store b/DailyDozen/DailyDozen/Details/.DS_Store new file mode 100644 index 00000000..df592eaa Binary files /dev/null and b/DailyDozen/DailyDozen/Details/.DS_Store differ diff --git a/DailyDozen/DailyDozen/Details/Controllers/DetailsDataProvider.swift b/DailyDozen/DailyDozen/Details/Controllers/DetailsDataProvider.swift new file mode 100644 index 00000000..0b733dba --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Controllers/DetailsDataProvider.swift @@ -0,0 +1,67 @@ +// +// DetailsDataProvider.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 31.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class DetailsDataProvider: NSObject, UITableViewDataSource { + + // MARK: - Nested + private struct Keys { + static let sizesID = "detailsSizesCell" + static let typesID = "detailsTypesCell" + } + + var viewModel: DetailViewModel! + + // MARK: - UITableViewDataSource + func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + guard let sectionType = DetailsSection(rawValue: section) else { + fatalError("There should be a section type") + } + switch sectionType { + case .sizes: + return viewModel.sizesCount + case .types: + return viewModel.typesCount + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let sectionType = DetailsSection(rawValue: indexPath.section) else { + fatalError("There should be a section type") + } + switch sectionType { + + case .sizes: + guard + let cell = tableView + .dequeueReusableCell(withIdentifier: Keys.sizesID) as? SizesCell + else { return UITableViewCell() } + + cell + .configure(title: viewModel.sizeDescription(for: indexPath.row)) + return cell + + case .types: + guard + let cell = tableView + .dequeueReusableCell(withIdentifier: Keys.typesID) as? TypesCell + else { return UITableViewCell() } + + cell + .configure(title: viewModel.typeData(for: indexPath.row).name, + useLink: viewModel.typeData(for: indexPath.row).hasLink, + tag: indexPath.row) + return cell + } + } +} diff --git a/DailyDozen/DailyDozen/Details/Controllers/DetailsViewController.swift b/DailyDozen/DailyDozen/Details/Controllers/DetailsViewController.swift new file mode 100644 index 00000000..24d40ea6 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Controllers/DetailsViewController.swift @@ -0,0 +1,135 @@ +// +// DetailsViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 31.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +// MARK: - Builder +class DetailsBuilder { + + // MARK: - Nested + struct Keys { + static let storyboard = "Details" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Parameter item: An item name. + /// - Returns: The initial view controller in the storyboard. + static func instantiateController(with item: String) -> DetailsViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() as? DetailsViewController + else { fatalError("There should be a controller") } + + viewController.setViewModel(for: item) + + return viewController + } +} + +// MARK: - Controller +class DetailsViewController: UIViewController { + + // MARK: - Nested + private struct Keys { + static let videos = "VIDEOS" + } + + // MARK: - Outlets + @IBOutlet private weak var tableView: UITableView! + @IBOutlet private weak var dataProvider: DetailsDataProvider! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var imageView: UIImageView! + + // MARK: - UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + tableView.dataSource = dataProvider + tableView.delegate = self + + navigationItem + .rightBarButtonItem = UIBarButtonItem(title: Keys.videos, + style: .done, + target: self, + action: #selector(barItemPressed)) + + imageView.image = dataProvider.viewModel.image + titleLabel.text = dataProvider.viewModel.itemTitle + } + + // MARK: - Methods + /// Sets a view model for the current item. + /// + /// - Parameter item: The current item name. + func setViewModel(for item: String) { + dataProvider.viewModel = TextsProvider.shared.loadDetail(for: item) + } + + /// Opens the main topic url in the browser. + @objc private func barItemPressed() { + UIApplication.shared + .open(dataProvider.viewModel.topicURL, + options: [:], + completionHandler: nil) + } + + // MARK: - Actions + /// Updates the tableView for the current unit type. + /// + /// - Parameter sender: The button. + @IBAction private func unitsChanged(_ sender: UIButton) { + let sectionIndex = DetailsSection.sizes.rawValue + guard + let text = sender.titleLabel?.text?.lowercased(), + let currentUnitsType = UnitsType(rawValue: text), + let indexPaths = tableView.indexPathsForRows(in: tableView.rect(forSection: sectionIndex)) + else { return } + + let newUnitsType = currentUnitsType.toggledType + let title = newUnitsType.title + sender.setTitle(title, for: .normal) + dataProvider.viewModel.unitsType = newUnitsType + tableView.reloadRows(at: indexPaths, with: .fade) + } + + /// Opens the type topic url in the browser. + /// + /// - Parameter sender: The button. + @IBAction private func linkButtonPressed(_ sender: UIButton) { + guard let url = dataProvider.viewModel.typeTopicURL(for: sender.tag) else { return } + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } +} + +// MARK: - UITableViewDelegate +extension DetailsViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + guard let sectionType = DetailsSection(rawValue: indexPath.section) else { + fatalError("There should be a section type") + } + return sectionType.rowHeight + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard let sectionType = DetailsSection(rawValue: section) else { + fatalError("There should be a section type") + } + return sectionType.headerHeigh + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let sectionType = DetailsSection(rawValue: section) else { + fatalError("There should be a section type") + } + return sectionType.headerView + } +} diff --git a/DailyDozen/DailyDozen/Details/Models/Detail.swift b/DailyDozen/DailyDozen/Details/Models/Detail.swift new file mode 100644 index 00000000..54cf37f3 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Models/Detail.swift @@ -0,0 +1,24 @@ +// +// Detail.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +struct Detail { + + // MARK: - Properties + var metricSizes: [String] + var imperialSizes: [String] + var types: [[String: String]] + + // MARK: - Inits + init(metricSizes: [String], imperialSizes: [String], types: [[String: String]]) { + self.metricSizes = metricSizes + self.imperialSizes = imperialSizes + self.types = types + } +} diff --git a/DailyDozen/DailyDozen/Details/Storyboards/Details.storyboard b/DailyDozen/DailyDozen/Details/Storyboards/Details.storyboard new file mode 100644 index 00000000..1644c264 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Storyboards/Details.storyboard @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + HelveticaNeue + HelveticaNeue-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Details/Storyboards/SizesHeader.xib b/DailyDozen/DailyDozen/Details/Storyboards/SizesHeader.xib new file mode 100644 index 00000000..e5774c73 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Storyboards/SizesHeader.xib @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + HelveticaNeue + HelveticaNeue-Bold + HelveticaNeue-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Details/Storyboards/TypesHeader.xib b/DailyDozen/DailyDozen/Details/Storyboards/TypesHeader.xib new file mode 100644 index 00000000..7d7270ed --- /dev/null +++ b/DailyDozen/DailyDozen/Details/Storyboards/TypesHeader.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + HelveticaNeue-Bold + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Details/SupportingFiles/DetailsSection.swift b/DailyDozen/DailyDozen/Details/SupportingFiles/DetailsSection.swift new file mode 100644 index 00000000..1cb4a48b --- /dev/null +++ b/DailyDozen/DailyDozen/Details/SupportingFiles/DetailsSection.swift @@ -0,0 +1,61 @@ +// +// SectionType.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 01.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +enum DetailsSection: Int { + + private struct Nibs { + static let sizesHeader = "SizesHeader" + static let typesHeader = "TypesHeader" + } + + private struct Strings { + static let sizesHeader = "Serving Sizes" + static let typesHeader = "Types" + } + + case sizes, types + + var rowHeight: CGFloat { + switch self { + case .sizes, .types: + return 75 + } + } + + var headerHeigh: CGFloat { + switch self { + case .sizes: + return 75 + case .types: + return 50 + } + } + + var headerView: UIView? { + switch self { + case .sizes: + return Bundle.main + .loadNibNamed(Nibs.sizesHeader, owner: nil)?.first as? UIView + case .types: + return Bundle.main + .loadNibNamed(Nibs.typesHeader, owner: nil)?.first as? UIView + } + } + + var title: String? { + switch self { + case .sizes: + return Strings.sizesHeader + case .types: + return Strings.typesHeader + } + } + +} diff --git a/DailyDozen/DailyDozen/Details/SupportingFiles/UnitsType.swift b/DailyDozen/DailyDozen/Details/SupportingFiles/UnitsType.swift new file mode 100644 index 00000000..2f035a08 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/SupportingFiles/UnitsType.swift @@ -0,0 +1,24 @@ +// +// UnitsType.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 13.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +enum UnitsType: String { + + case metric, imperial + + /// Returns an uppercase version of the rawValue for the current type. + var title: String { + return self.rawValue.uppercased() + } + + /// Returns toggled type for the current type. + var toggledType: UnitsType { + return self == .metric ? UnitsType.imperial : UnitsType.metric + } +} diff --git a/DailyDozen/DailyDozen/Details/View/SizesCell.swift b/DailyDozen/DailyDozen/Details/View/SizesCell.swift new file mode 100644 index 00000000..a63b5fc2 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/View/SizesCell.swift @@ -0,0 +1,23 @@ +// +// SizesCell.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 10.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class SizesCell: UITableViewCell { + + // MARK: - Outlets + @IBOutlet private weak var titleLabel: UILabel! + + // MARK: - Methods. + /// Sets the new title text. + /// + /// - Parameter title: The new title text. + func configure(title: String) { + titleLabel.text = title + } +} diff --git a/DailyDozen/DailyDozen/Details/View/TypesCell.swift b/DailyDozen/DailyDozen/Details/View/TypesCell.swift new file mode 100644 index 00000000..2da135d6 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/View/TypesCell.swift @@ -0,0 +1,29 @@ +// +// TypesCell.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 10.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class TypesCell: UITableViewCell { + + // MARK: - Outlets + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var linkButton: UIButton! + + // MARK: - Methods + /// Sets the cell data. + /// + /// - Parameters: + /// - title: The title text. + /// - useLink: The new state for the link button. + /// - tag: The button tag. + func configure(title: String, useLink: Bool, tag: Int) { + titleLabel.text = title + linkButton.isHidden = useLink + linkButton.tag = tag + } +} diff --git a/DailyDozen/DailyDozen/Details/ViewModels/DetailViewModel.swift b/DailyDozen/DailyDozen/Details/ViewModels/DetailViewModel.swift new file mode 100644 index 00000000..96b4f491 --- /dev/null +++ b/DailyDozen/DailyDozen/Details/ViewModels/DetailViewModel.swift @@ -0,0 +1,78 @@ +// +// DetailViewModel.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +struct DetailViewModel { + + // MARK: - Properties + private let detail: Detail + private let itemName: String + private let topic: String + + var unitsType = UnitsType.metric + + /// Returns the main topic url. + var topicURL: URL { + return LinksService.shared.link(forTopic: topic) + } + + /// Returns the number of items in the metric sizes. + var sizesCount: Int { + return detail.metricSizes.count + } + + /// Returns the number of items in the types. + var typesCount: Int { + return detail.types.count + } + /// Returns the item name. + var itemTitle: String { + return itemName + } + + /// Returns an image of the item. + var image: UIImage? { + return UIImage(named: itemName.lowercased().replacingOccurrences(of: " ", with: "_")) + } + + // MARK: - Inits + init(itemName: String, topic: String, metricSizes: [String], imperialSizes: [String], types: [[String: String]]) { + detail = Detail(metricSizes: metricSizes, imperialSizes: imperialSizes, types: types) + self.itemName = itemName + self.topic = topic + } + + // MARK: - Methods + /// Returns a size description for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: A description string. + func sizeDescription(for index: Int) -> String { + return unitsType == .metric ? detail.metricSizes[index] : detail.imperialSizes[index] + } + + /// Returns a tuple of the type name and type link state for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: A tuple of the type name and type link. + func typeData(for index: Int) -> (name: String, hasLink: Bool) { + let name = detail.types[index].keys.first ?? "" + let hasLink = detail.types[index].values.first == "" + return (name, hasLink) + } + + /// Returns the type topic for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: The type toipic url. + func typeTopicURL(for index: Int) -> URL? { + guard let topic = detail.types[index].values.first else { return nil } + return LinksService.shared.link(forTopic: topic) + } +} diff --git a/DailyDozen/DailyDozen/ItemHistory/Controllers/ItemHistoryViewController.swift b/DailyDozen/DailyDozen/ItemHistory/Controllers/ItemHistoryViewController.swift new file mode 100644 index 00000000..cf32793a --- /dev/null +++ b/DailyDozen/DailyDozen/ItemHistory/Controllers/ItemHistoryViewController.swift @@ -0,0 +1,97 @@ +// +// ItemHistoryViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 16.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import FSCalendar + +class ItemHistoryBuilder { + + // MARK: - Nested + private struct Keys { + static let storyboard = "ItemHistory" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Parameter title: An item name. + /// - Returns: The initial view controller in the storyboard. + static func instantiateController(with title: String, itemId: Int) -> UIViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() as? ItemHistoryViewController + else { fatalError("There should be a controller") } + viewController.title = title + viewController.itemId = itemId + + return viewController + } +} + +class ItemHistoryViewController: UIViewController { + + // MARK: - Nested + private struct Keys { + static let cell = "DateCell" + } + + // MARK: - Properties + private let realm = RealmProvider() + fileprivate var itemId = 0 + + // MARK: - Outlets + @IBOutlet private weak var calendarView: FSCalendar! + + // MARK: - UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + calendarView.delegate = self + calendarView.dataSource = self + calendarView.register(DateCell.self, forCellReuseIdentifier: Keys.cell) + } +} + +// MARK: - FSCalendarDataSource +extension ItemHistoryViewController: FSCalendarDataSource { + + func maximumDate(for calendar: FSCalendar) -> Date { + return Date() + } + + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + guard + let cell = calendar + .dequeueReusableCell(withIdentifier: Keys.cell, for: date, at: .current) as? DateCell + else { fatalError("There should be a cell") } + + guard date < Date() else { return cell } + + let states = realm.getDoze(for: date).items[itemId].states + let selectedStates = states.filter { $0 } + + cell.configure(for: selectedStates.count, maximum: states.count) + + return cell + } +} + +// MARK: - FSCalendarDelegate +extension ItemHistoryViewController: FSCalendarDelegate { + + func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { + guard + let viewController = navigationController? + .viewControllers[1] as? PagerViewController + else { return } + + viewController.updateDate(date) + navigationController?.popViewController(animated: true) + } +} diff --git a/DailyDozen/DailyDozen/ItemHistory/Storyboards/ItemHistory.storyboard b/DailyDozen/DailyDozen/ItemHistory/Storyboards/ItemHistory.storyboard new file mode 100644 index 00000000..4cddcfc3 --- /dev/null +++ b/DailyDozen/DailyDozen/ItemHistory/Storyboards/ItemHistory.storyboard @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + HelveticaNeue + HelveticaNeue-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/ItemHistory/Views/DateCell.swift b/DailyDozen/DailyDozen/ItemHistory/Views/DateCell.swift new file mode 100644 index 00000000..23b90a68 --- /dev/null +++ b/DailyDozen/DailyDozen/ItemHistory/Views/DateCell.swift @@ -0,0 +1,55 @@ +// +// DateCell.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 21.11.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import FSCalendar + +class DateCell: FSCalendarCell { + + // MARK: - Properties + private weak var borderView: UIView! + + // MARK: - Inits + required init!(coder aDecoder: NSCoder!) { + fatalError("init(coder:) has not been implemented") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + let borderView = UIView() + contentView.insertSubview(borderView, at: 1) + + self.borderView = borderView + } + + override func layoutSubviews() { + super.layoutSubviews() + + let dx = (titleLabel.bounds.width - titleLabel.bounds.height) / 2 + borderView.frame = titleLabel.bounds.insetBy(dx: dx + 2, dy: 2) + borderView.layer.cornerRadius = borderView.bounds.width / 2 + } + + // MARK: - Methods + /// Sets the borderView color. + /// + /// - Parameters: + /// - count: The current states count. + /// - maximum: The maximum of states. + func configure(for count: Int, maximum: Int) { + if count == maximum { + borderView.backgroundColor = UIColor.yellowColor + } else if count > 0 { + borderView.backgroundColor = UIColor.yellow + } else { + borderView.backgroundColor = UIColor.white + } + setNeedsLayout() + } +} diff --git a/DailyDozen/DailyDozen/Menu/Controllers/MenuViewController.swift b/DailyDozen/DailyDozen/Menu/Controllers/MenuViewController.swift new file mode 100644 index 00000000..345f7fc9 --- /dev/null +++ b/DailyDozen/DailyDozen/Menu/Controllers/MenuViewController.swift @@ -0,0 +1,76 @@ +// +// MenuViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 18.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +// MARK: - UITableViewController +class MenuViewController: UITableViewController { + + // MARK: - Nested + private struct Strings { + static let menu = "Menu" + } + + private struct Keys { + static let realm = "main.realm" + } + + // MARK: - UITableViewController + override func viewDidLoad() { + super.viewDidLoad() + + title = "" + + navigationController?.navigationBar.barTintColor = UIColor.greenColor + navigationController?.navigationBar.tintColor = UIColor.white + + let barItem = UIBarButtonItem(title: Strings.menu, style: .done, target: nil, action: nil) + barItem.tintColor = UIColor.white + navigationItem.setLeftBarButton(barItem, animated: false) + } + + /// Presents share services. + private func share() { + let activityViewController = UIActivityViewController(activityItems: [URL.inDocuments(for: Keys.realm)], applicationActivities: nil) + activityViewController.popoverPresentationController?.sourceView = view + present(activityViewController, animated: true, completion: nil) + } +} + +// MARK: - UITableViewDelegate +extension MenuViewController { + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let menuItem = MenuItem(rawValue: indexPath.row) else { return } + + if let link = menuItem.link { + UIApplication.shared + .open(LinksService.shared.link(forMenu: link), + options: [:], + completionHandler: nil) + dismiss(animated: false) + } else if let controller = menuItem.controller { + splitViewController?.showDetailViewController(controller, sender: nil) + } else { + share() + } + tableView.selectRow(at: nil, animated: false, scrollPosition: .none) + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + super.tableView(tableView, heightForHeaderInSection: section) + + return 0.1 + } + + override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + super.tableView(tableView, heightForFooterInSection: section) + + return 0.1 + } +} diff --git a/DailyDozen/DailyDozen/Menu/Storyboards/Menu.storyboard b/DailyDozen/DailyDozen/Menu/Storyboards/Menu.storyboard new file mode 100644 index 00000000..31715604 --- /dev/null +++ b/DailyDozen/DailyDozen/Menu/Storyboards/Menu.storyboard @@ -0,0 +1,589 @@ + + + + + + + + + + + + + HelveticaNeue + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Menu/SupportingFiles/MenuItem.swift b/DailyDozen/DailyDozen/Menu/SupportingFiles/MenuItem.swift new file mode 100644 index 00000000..b4e9fd17 --- /dev/null +++ b/DailyDozen/DailyDozen/Menu/SupportingFiles/MenuItem.swift @@ -0,0 +1,56 @@ +// +// MenuItem.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 31.01.2018. +// Copyright © 2018 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +enum MenuItem: Int { + + // MARK: - Nested + private struct Links { + static let videos = "videos" + static let book = "book" + static let cookbook = "cookbook" + static let donate = "donate" + static let subscribe = "subscribe" + static let source = "open-source" + } + + case servings, videos, book, cookbook, donate, subscribe, source, settings, backup, about + + var link: String? { + switch self { + case .servings, .settings, .backup, .about: + return nil + case .videos: + return Links.videos + case .book: + return Links.book + case .cookbook: + return Links.cookbook + case .donate: + return Links.donate + case .subscribe: + return Links.subscribe + case .source: + return Links.source + } + } + + var controller: UIViewController? { + switch self { + case .servings: + return PagerBuilder.instantiateController() + case .about: + return AboutBuilder.instantiateController() + case .settings: + return ReminderBuilder.instantiateController() + case .videos, .book, .cookbook, .donate, .subscribe, .source, .backup: + return nil + } + } +} diff --git a/DailyDozen/DailyDozen/Reminder/Controllers/ReminderViewController.swift b/DailyDozen/DailyDozen/Reminder/Controllers/ReminderViewController.swift new file mode 100644 index 00000000..e6a6b63e --- /dev/null +++ b/DailyDozen/DailyDozen/Reminder/Controllers/ReminderViewController.swift @@ -0,0 +1,126 @@ +// +// ReminderViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 12.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import UserNotifications + +// MARK: - Builder +class ReminderBuilder { + + // MARK: - Nested + private struct Keys { + static let storyboard = "Reminder" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Returns: The initial view controller in the storyboard. + static func instantiateController() -> ReminderViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() as? ReminderViewController + else { fatalError("There should be a controller") } + viewController.title = "Daily Reminder Settings" + + return viewController + } +} + +// MARK: - Controller +class ReminderViewController: UIViewController { + + // MARK: - Nested + private struct Keys { + static let canNotificate = "canNotificate" + static let hour = "hour" + static let minute = "minute" + static let sound = "sound" + static let imgID = "imgID" + static let requestID = "requestID" + } + + private struct Content { + static let title = "DailyDozen app." + static let subtitle = "Do you remember about the app?" + static let body = "Update your servings for today!" + static let img = "dr_greger" + static let png = "png" + } + + @IBOutlet private weak var settingsPanel: RoundedView! + @IBOutlet private weak var datePicker: UIDatePicker! + @IBOutlet private weak var reminderSwitch: UISwitch! + @IBOutlet private weak var soundSwitch: UISwitch! + + override func viewDidLoad() { + super.viewDidLoad() + + let canNotificate = UserDefaults.standard.bool(forKey: Keys.canNotificate) + reminderSwitch.isOn = canNotificate + settingsPanel.isHidden = !canNotificate + + datePicker.date.hour = UserDefaults.standard.integer(forKey: Keys.hour) + datePicker.date.minute = UserDefaults.standard.integer(forKey: Keys.minute) + + soundSwitch.isOn = UserDefaults.standard.bool(forKey: Keys.sound) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + guard reminderSwitch.isOn else { return } + + UserDefaults.standard.set(soundSwitch.isOn, forKey: Keys.sound) + + if UserDefaults.standard.integer(forKey: Keys.hour) != datePicker.date.hour || + UserDefaults.standard.integer(forKey: Keys.minute) != datePicker.date.minute { + UserDefaults.standard.set(datePicker.date.hour, forKey: Keys.hour) + UserDefaults.standard.set(datePicker.date.minute, forKey: Keys.minute) + + UNUserNotificationCenter.current().removeAllPendingNotificationRequests() + + let content = UNMutableNotificationContent() + content.title = Content.title + content.subtitle = Content.subtitle + content.body = Content.body + content.badge = 1 + + guard + let url = Bundle.main.url(forResource: Content.img, withExtension: Content.png), + let attachment = try? UNNotificationAttachment(identifier: Keys.imgID, url: url, options: nil) + else { return } + + content.attachments.append(attachment) + + if soundSwitch.isOn { content.sound = UNNotificationSound.default() } + + var dateComponents = DateComponents() + dateComponents.hour = UserDefaults.standard.integer(forKey: Keys.hour) + dateComponents.minute = UserDefaults.standard.integer(forKey: Keys.minute) + + let dateTrigget = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) + + let request = UNNotificationRequest(identifier: Keys.requestID, content: content, trigger: dateTrigget) + + UNUserNotificationCenter.current().add(request) { (error) in + if let error = error { + print(error.localizedDescription) + } + } + } + } + + @IBAction private func reminderSwithed(_ sender: UISwitch) { + settingsPanel.isHidden = !sender.isOn + UserDefaults.standard.set(sender.isOn, forKey: Keys.canNotificate) + UNUserNotificationCenter.current().removeAllPendingNotificationRequests() + } + +} diff --git a/DailyDozen/DailyDozen/Reminder/Storyboards/Reminder.storyboard b/DailyDozen/DailyDozen/Reminder/Storyboards/Reminder.storyboard new file mode 100644 index 00000000..6685086e --- /dev/null +++ b/DailyDozen/DailyDozen/Reminder/Storyboards/Reminder.storyboard @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + HelveticaNeue + HelveticaNeue-Bold + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Servings/.DS_Store b/DailyDozen/DailyDozen/Servings/.DS_Store new file mode 100644 index 00000000..0a45ccb1 Binary files /dev/null and b/DailyDozen/DailyDozen/Servings/.DS_Store differ diff --git a/DailyDozen/DailyDozen/Servings/Controllers/PagerViewController.swift b/DailyDozen/DailyDozen/Servings/Controllers/PagerViewController.swift new file mode 100644 index 00000000..f02d558e --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Controllers/PagerViewController.swift @@ -0,0 +1,135 @@ +// +// PagerViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 18.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import SimpleAnimation + +// MARK: - Builder +class PagerBuilder { + + // MARK: - Nested + struct Keys { + static let storyboard = "Pager" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Returns: The initial view controller in the storyboard. + static func instantiateController() -> UIViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() + else { fatalError("There should be a controller") } + + return viewController + } +} + +// MARK: - Controller +class PagerViewController: UIViewController { + + // MARK: - Properties + private var currentDate = Date() { + didSet { + if currentDate.isInCurrentDayWith(Date()) { + backButton.superview?.isHidden = true + dateButton.setTitle("Today", for: .normal) + } else { + backButton.superview?.isHidden = false + dateButton.setTitle(datePicker.date.dateString(for: .long), for: .normal) + } + } + } + + // MARK: - Outlets + @IBOutlet private weak var dateButton: UIButton! { + didSet { + dateButton.layer.borderWidth = 1 + dateButton.layer.borderColor = dateButton.titleColor(for: .normal)?.cgColor + dateButton.layer.cornerRadius = 5 + } + } + @IBOutlet private weak var datePicker: UIDatePicker! { + didSet { + datePicker.maximumDate = Date() + } + } + + @IBOutlet private weak var backButton: UIButton! + + // MARK: - UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + title = "Servings" + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white] + } + + // MARK: - Methods + /// Updates UI for the current date. + /// + /// - Parameter date: The current date. + func updateDate(_ date: Date) { + currentDate = date + datePicker.setDate(date, animated: false) + + guard let viewController = childViewControllers.first as? ServingsViewController else { return } + viewController.view.fadeOut().fadeIn() + viewController.setViewModel(for: currentDate) + } + + // MARK: - Actions + @IBAction private func dateButtonPressed(_ sender: UIButton) { + datePicker.isHidden = false + dateButton.isHidden = true + } + + @IBAction private func dateChanged(_ sender: UIDatePicker) { + dateButton.isHidden = false + datePicker.isHidden = true + currentDate = datePicker.date + + guard let viewController = childViewControllers.first as? ServingsViewController else { return } + viewController.view.fadeOut().fadeIn() + viewController.setViewModel(for: datePicker.date) + } + + @IBAction private func viewSwipped(_ sender: UISwipeGestureRecognizer) { + let interval = sender.direction == .left ? -1 : 1 + let currentDate = datePicker.date.adding(.day, value: interval) + + let today = Date() + + guard let date = currentDate, date <= today else { return } + + datePicker.setDate(date, animated: false) + + self.currentDate = datePicker.date + + guard let viewController = childViewControllers.first as? ServingsViewController else { return } + + if sender.direction == .left { + viewController.view.slideOut(x: -view.frame.width).slideIn(x: view.frame.width) + } else { + viewController.view.slideOut(x: view.frame.width).slideIn(x: -view.frame.width) + } + + viewController.setViewModel(for: datePicker.date) + } + + @IBAction private func backButtonPressed(_ sender: UIButton) { + updateDate(Date()) + } +} diff --git a/DailyDozen/DailyDozen/Servings/Controllers/ServingsDataProvider.swift b/DailyDozen/DailyDozen/Servings/Controllers/ServingsDataProvider.swift new file mode 100644 index 00000000..ba5f2c94 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Controllers/ServingsDataProvider.swift @@ -0,0 +1,85 @@ +// +// ServingsDataProvider.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 23.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class ServingsDataProvider: NSObject, UITableViewDataSource { + + // MARK: - Nested + private struct Keys { + static let servingsCell = "servingsCell" + static let doseCell = "doseCell" + } + + var viewModel: DozeViewModel! + + // MARK: - Servings UITableViewDataSource + func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + guard let servingsSection = ServingsSection(rawValue: section) else { + fatalError("There should be a section type") + } + return servingsSection.numberOfRowsInSection(with: viewModel.count) + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let realm = RealmProvider() + guard + let cell = tableView + .dequeueReusableCell(withIdentifier: Keys.servingsCell) as? ServingsCell, + let servingsSection = ServingsSection(rawValue: indexPath.section) else { + fatalError("There should settings") + } + var index = indexPath.row + if servingsSection == .vitamin { + index += tableView.numberOfRows(inSection: 0) + } + + var streak = viewModel.itemStates(for: index).count == viewModel.itemStates(for: index).filter { $0 }.count ? 1 : 0 + + if streak > 0 { + streak += realm + .getDoze(for: viewModel.dozeDate.adding(.day, value: -1)!) + .items[index].streak + } + + cell.configure( + with: viewModel.itemInfo(for: index).name, + tag: index, + imageName: viewModel.imageName(for: index), + streak: streak) + + let id = viewModel.itemID(for: index) + realm.updateStreak(streak, with: id) + + return cell + } +} + +// MARK: - States UICollectionViewDataSource +extension ServingsDataProvider: UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + let states = viewModel.itemStates(for: collectionView.tag) + return states.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard + let cell = collectionView + .dequeueReusableCell(withReuseIdentifier: Keys.doseCell, for: indexPath) as? StateCell else { + fatalError("There should be a cell") + + } + cell.configure(with: viewModel.itemStates(for: collectionView.tag)[indexPath.row]) + return cell + } +} diff --git a/DailyDozen/DailyDozen/Servings/Controllers/ServingsViewController.swift b/DailyDozen/DailyDozen/Servings/Controllers/ServingsViewController.swift new file mode 100644 index 00000000..68fcdf41 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Controllers/ServingsViewController.swift @@ -0,0 +1,177 @@ +// +// ServingsViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 18.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import StoreKit + +class ServingsViewController: UIViewController { + + // MARK: - Nested + private struct Keys { + static let countMaximum = 24 + } + + // MARK: - Outlets + @IBOutlet private weak var dataProvider: ServingsDataProvider! + @IBOutlet private weak var tableView: UITableView! + @IBOutlet private weak var countLabel: UILabel! + @IBOutlet private weak var starImage: UIImageView! + + // MARK: - Properties + private let realm = RealmProvider() + + private var statesCount = 0 { + didSet { + countLabel.text = statesCountString + if statesCount == Keys.countMaximum { + starImage.popIn() + SKStoreReviewController.requestReview() + } else { + starImage.popOut() + } + } + } + private var statesCountString: String { + return "\(statesCount) out of \(Keys.countMaximum)" + } + + // MARK: - UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + setViewModel(for: Date()) + + tableView.dataSource = dataProvider + tableView.delegate = self + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + appDelegate.realmDelegate = self + } + + // MARK: - Methods + /// Sets a view model for the current date. + /// + /// - Parameter item: The current date. + func setViewModel(for date: Date) { + dataProvider.viewModel = DozeViewModel(doze: realm.getDoze(for: date)) + statesCount = 0 + let selectedStates = (0 ... dataProvider.viewModel.count - 3) + .map { dataProvider.viewModel.itemStates(for: $0) } + .map { $0.filter { $0 }.count } + statesCount = selectedStates.reduce(0) { $0 + $1 } + tableView.reloadData() + } + + // MARK: - Actions + @IBAction private func infoPressed(_ sender: UIButton) { + let itemInfo = dataProvider.viewModel.itemInfo(for: sender.tag) + + guard !itemInfo.isVitamin else { + let url = dataProvider.viewModel.topicURL(for: itemInfo.name) + UIApplication.shared + .open(url, + options: [:], + completionHandler: nil) + return + } + let viewController = DetailsBuilder.instantiateController(with: itemInfo.name) + navigationController?.pushViewController(viewController, animated: true) + } + + @IBAction private func calendarPressed(_ sender: UIButton) { + let name = dataProvider.viewModel.itemInfo(for: sender.tag).name + let viewController = ItemHistoryBuilder.instantiateController(with: name, itemId: sender.tag) + navigationController?.pushViewController(viewController, animated: true) + } + + @IBAction private func vitaminHeaderPressed(_ sender: UIButton) { + let alert = AlertBuilder.instantiateController(for: .vitamin) + present(alert, animated: true, completion: nil) + } + + @IBAction private func historyPressed(_ sender: UIButton) { + let viewController = ServingsHistoryBuilder.instantiateController() + navigationController?.pushViewController(viewController, animated: true) + } +} + +// MARK: - Servings UITableViewDelegate +extension ServingsViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return ServingsSection.main.rowHeight + } + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + guard let servingsCell = cell as? ServingsCell else { return } + servingsCell.stateCollection.delegate = self + servingsCell.stateCollection.dataSource = dataProvider + servingsCell.stateCollection.reloadData() + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard let servingsSection = ServingsSection(rawValue: section) else { + fatalError("There should be a section type") + } + return servingsSection.headerHeight + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return ServingsSection.main.footerHeight + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let servingsSection = ServingsSection(rawValue: section) else { + fatalError("There should be a section type") + } + return servingsSection.headerView + } +} + +// MARK: - States UICollectionViewDelegate +extension ServingsViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + var states = dataProvider.viewModel.itemStates(for: collectionView.tag) + let newState = !states[indexPath.row] + states[indexPath.row] = newState + guard let cell = collectionView.cellForItem(at: indexPath) as? StateCell else { + fatalError("There should be a cell") + } + cell.configure(with: states[indexPath.row]) + let id = dataProvider.viewModel.itemID(for: collectionView.tag) + realm.saveStates(states, with: id) + + var streak = states.count == states.filter { $0 }.count ? 1 : 0 + + if streak > 0 { + streak += realm + .getDoze(for: dataProvider.viewModel.dozeDate.adding(.day, value: -1)!) + .items[collectionView.tag].streak + } + + realm.updateStreak(streak, with: id) + + tableView.reloadData() + + guard !dataProvider.viewModel.itemInfo(for: collectionView.tag).isVitamin else { return } + + if newState { + statesCount += 1 + } else { + statesCount -= 1 + } + } +} + +extension ServingsViewController: RealmDelegate { + + func didUpdateFile() { + navigationController?.popViewController(animated: false) + } +} diff --git a/DailyDozen/DailyDozen/Servings/Models/Doze.swift b/DailyDozen/DailyDozen/Servings/Models/Doze.swift new file mode 100644 index 00000000..b14fe0fb --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Models/Doze.swift @@ -0,0 +1,28 @@ +// +// Doze.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 24.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation +import RealmSwift + +class Doze: Object { + + @objc dynamic var date = Date() + @objc dynamic var id = UUID().uuidString + + let items = List() + + convenience init(date: Date, items: [Item]) { + self.init() + self.date = date + self.items.append(objectsIn: items) + } + + override static func primaryKey() -> String? { + return "id" + } +} diff --git a/DailyDozen/DailyDozen/Servings/Models/Item.swift b/DailyDozen/DailyDozen/Servings/Models/Item.swift new file mode 100644 index 00000000..79acb88d --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Models/Item.swift @@ -0,0 +1,29 @@ +// +// Entity.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 23.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation +import RealmSwift + +class Item: Object { + + @objc dynamic var name = "" + @objc dynamic var id = UUID().uuidString + @objc dynamic var streak = 0 + let states = List() + + convenience init(name: String, states: [Bool], streak: Int = 0) { + self.init() + self.name = name + self.streak = streak + self.states.append(objectsIn: states) + } + + override static func primaryKey() -> String? { + return "id" + } +} diff --git a/DailyDozen/DailyDozen/Servings/Storyboards/Pager.storyboard b/DailyDozen/DailyDozen/Servings/Storyboards/Pager.storyboard new file mode 100644 index 00000000..10ab343e --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Storyboards/Pager.storyboard @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + Helvetica + + + HelveticaNeue + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Servings/Storyboards/Servings.storyboard b/DailyDozen/DailyDozen/Servings/Storyboards/Servings.storyboard new file mode 100644 index 00000000..31a88619 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Storyboards/Servings.storyboard @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + Helvetica-Bold + + + HelveticaNeue + HelveticaNeue-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Servings/Storyboards/VitaminsHeader.xib b/DailyDozen/DailyDozen/Servings/Storyboards/VitaminsHeader.xib new file mode 100644 index 00000000..5bf1b0f3 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Storyboards/VitaminsHeader.xib @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + HelveticaNeue + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/Servings/SupportingFiles/ServingsSection.swift b/DailyDozen/DailyDozen/Servings/SupportingFiles/ServingsSection.swift new file mode 100644 index 00000000..112616a5 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/SupportingFiles/ServingsSection.swift @@ -0,0 +1,58 @@ +// +// ServingsSection.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 15.11.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +enum ServingsSection: Int { + + private struct Keys { + static let vitaminsHeader = "VitaminsHeader" + } + + case main, vitamin + + private var vitaminsCount: Int { + return 2 + } + + var rowHeight: CGFloat { + return 100 + } + + var headerHeight: CGFloat { + switch self { + case .main: + return 0.1 + case .vitamin: + return 50 + } + } + + var footerHeight: CGFloat { + return 0.1 + } + + var headerView: UIView? { + switch self { + case .main: + return nil + case .vitamin: + return Bundle.main + .loadNibNamed(Keys.vitaminsHeader, owner: nil)?.first as? UIView + } + } + + func numberOfRowsInSection(with count: Int) -> Int { + switch self { + case .main: + return count - vitaminsCount + case .vitamin: + return vitaminsCount + } + } +} diff --git a/DailyDozen/DailyDozen/Servings/ViewModel/DozeViewModel.swift b/DailyDozen/DailyDozen/Servings/ViewModel/DozeViewModel.swift new file mode 100644 index 00000000..b6b94bb3 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/ViewModel/DozeViewModel.swift @@ -0,0 +1,82 @@ +// +// DozeViewModel.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 24.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation + +class DozeViewModel { + + // MARK: - Properties + private let doze: Doze + + /// Returns the number of items in the doze. + var count: Int { + return doze.items.count + } + + /// Returns a doze date. + var dozeDate: Date { + return doze.date + } + + // MARK: - Inits + init(doze: Doze) { + self.doze = doze + } + + // MARK: - Methods + /// Returns an item name and type in the doze for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: A tuple with the item name and type. + func itemInfo(for index: Int) -> (name: String, isVitamin: Bool) { + let name = doze.items[index].name + return (name, name.contains("Vitamin")) + } + + /// Returns an item streak count for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: The streak count. + func itemStreak(for index: Int) -> Int { + return doze.items[index].streak + } + + /// Returns a url for the current item name. + /// + /// - Parameter itemName: The item name. + /// - Returns: A url. + func topicURL(for itemName: String) -> URL { + let topic = TextsProvider.shared.getTopic(for: itemName) + return LinksService.shared.link(forTopic: topic) + } + + /// Returns item states in the doze for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: The states array. + func itemStates(for index: Int) -> [Bool] { + return Array(doze.items[index].states) + } + + /// Returns an item ID in the doze for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: The item id. + func itemID(for index: Int) -> String { + return doze.items[index].id + } + + /// Returns an image name for the current index. + /// + /// - Parameter index: The current index. + /// - Returns: The image name. + func imageName(for index: Int) -> String { + let name = "ic_\(itemInfo(for: index).name.lowercased().replacingOccurrences(of: " ", with: "_"))" + return name + } +} diff --git a/DailyDozen/DailyDozen/Servings/Views/ServingsCell.swift b/DailyDozen/DailyDozen/Servings/Views/ServingsCell.swift new file mode 100644 index 00000000..dd551b41 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Views/ServingsCell.swift @@ -0,0 +1,41 @@ +// +// ServingsCell.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 23.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class ServingsCell: UITableViewCell { + + // MARK: - Outlets + @IBOutlet private weak var itemImage: UIImageView! + @IBOutlet private weak var streakLabel: UILabel! + @IBOutlet private weak var itemLabel: UILabel! + @IBOutlet weak var stateCollection: UICollectionView! + @IBOutlet private weak var infoButton: UIButton! + @IBOutlet private weak var calendarButton: UIButton! + + // MARK: - Methods + /// Sets the cell with the current name, image name and tag. + /// + /// - Parameter name: The current name. + /// - Parameter tag: The current tag. + /// - Parameter tag: The image name tag. + func configure(with name: String, tag: Int, imageName: String, streak: Int = 0) { + itemLabel.text = name + stateCollection.tag = tag + infoButton.tag = tag + calendarButton.tag = tag + itemImage.image = UIImage(named: imageName) + + if streak > 1 { + streakLabel.text = "\(streak) days" + streakLabel.superview?.isHidden = false + } else { + streakLabel.superview?.isHidden = true + } + } +} diff --git a/DailyDozen/DailyDozen/Servings/Views/StateCell.swift b/DailyDozen/DailyDozen/Servings/Views/StateCell.swift new file mode 100644 index 00000000..ed5432d3 --- /dev/null +++ b/DailyDozen/DailyDozen/Servings/Views/StateCell.swift @@ -0,0 +1,25 @@ +// +// DoseCell.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 25.10.17. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import UICheckbox_Swift + +class StateCell: UICollectionViewCell { + + // MARK: - Outlets + @IBOutlet private weak var checkbox: UICheckbox! + + // MARK: - Methods + /// Sets the checkbox with the current state. + /// + /// - Parameter state: The current state. + func configure(with state: Bool) { + checkbox.isSelected = state + checkbox.layer.borderColor = state ? UIColor.greenColor.cgColor : UIColor.lightGrayColor.cgColor + } +} diff --git a/DailyDozen/DailyDozen/ServingsHistory/Controllers/ServingsHistoryViewController.swift b/DailyDozen/DailyDozen/ServingsHistory/Controllers/ServingsHistoryViewController.swift new file mode 100644 index 00000000..36db074c --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/Controllers/ServingsHistoryViewController.swift @@ -0,0 +1,193 @@ +// +// ServingsHistoryViewController.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 22.11.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import Charts + +// MARK: - Nested +enum TimeScale: Int { + case day, month, year +} + +class ServingsHistoryBuilder { + + // MARK: - Nested + private struct Keys { + static let storyboard = "ServingsHistory" + } + + // MARK: - Methods + /// Instantiates and returns the initial view controller for a storyboard. + /// + /// - Returns: The initial view controller in the storyboard. + static func instantiateController() -> ServingsHistoryViewController { + let storyboard = UIStoryboard(name: Keys.storyboard, bundle: nil) + guard + let viewController = storyboard + .instantiateInitialViewController() as? ServingsHistoryViewController + else { fatalError("There should be a controller") } + viewController.title = "Servings History" + + return viewController + } +} + +class ServingsHistoryViewController: UIViewController { + + // MARK: - Outlets + @IBOutlet private weak var chartView: ChartView! + @IBOutlet private weak var controlPanel: ControlPanel! + @IBOutlet private weak var scaleControl: UISegmentedControl! + + // MARK: - Properties + private var viewModel: ServingsHistoryViewModel! + private var currentTimeScale = TimeScale.day + + private var chartSettings: (year: Int, month: Int)! { + didSet { + chartView.clear() + + if currentTimeScale == .day { + controlPanel.isHidden = false + controlPanel.superview?.isHidden = false + + var canLeft = true + if chartSettings.month == 0, chartSettings.year == 0 { + canLeft = false + } + + var canRight = true + + if chartSettings.year == viewModel.lastYearIndex, + chartSettings.month == viewModel.lastMonthIndex(for: viewModel.lastYearIndex) { + canRight = false + } + + controlPanel.configure(canSwitch: (left: canLeft, right: canRight)) + + let data = viewModel.monthData(yearIndex: chartSettings.year, monthIndex: chartSettings.month) + + controlPanel.setLabels(month: data.month, year: viewModel.yearName(yearIndex: chartSettings.year)) + + chartView.configure(with: data.map, for: currentTimeScale) + } else if currentTimeScale == .month { + controlPanel.isHidden = false + controlPanel.superview?.isHidden = false + + let canLeft = chartSettings.year != 0 + let canRight = chartSettings.year != viewModel.lastYearIndex + controlPanel.configure(canSwitch: (left: canLeft, right: canRight)) + + let data = viewModel.yearlyData(yearIndex: chartSettings.year) + + controlPanel.setLabels(year: data.year) + + chartView.configure(with: data.map, for: currentTimeScale) + } else { + controlPanel.isHidden = true + controlPanel.superview?.isHidden = true + chartView.configure(with: viewModel.fullDataMap(), for: currentTimeScale) + } + } + } + + // MARK: - UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + chartView.xAxis.valueFormatter = self + setViewModel() + } + + // MARK: - Methods + private func setViewModel() { + let realm = RealmProvider() + + let results = realm + .getDozes() + .sorted(byKeyPath: "date") + guard results.count > 0 else { + controlPanel.isHidden = true + scaleControl.isEnabled = false + controlPanel.superview?.isHidden = true + return + } + + viewModel = ServingsHistoryViewModel(results) + let lastYearIndex = viewModel.lastYearIndex + + chartSettings = (lastYearIndex, viewModel.lastMonthIndex(for: lastYearIndex)) + } + + // MARK: - Actions + @IBAction private func toFirstButtonPressed(_ sender: UIButton) { + chartSettings = (0, 0) + } + + @IBAction private func toPreviousButtonPressed(_ sender: UIButton) { + if chartSettings.month > 0 && currentTimeScale == .day { + chartSettings.month -= 1 + } else if chartSettings.year > 0 { + let year = chartSettings.year - 1 + let month = viewModel.lastMonthIndex(for: year) + chartSettings = (year, month) + } + } + + @IBAction private func toNextButtonPressed(_ sender: UIButton) { + if chartSettings.month < viewModel.lastMonthIndex(for: chartSettings.year) && currentTimeScale == .day { + chartSettings.month += 1 + } else if chartSettings.year < viewModel.lastYearIndex { + let year = chartSettings.year + 1 + let month = 0 + chartSettings = (year, month) + } + } + + @IBAction private func toLastButtonPressed(_ sender: UIButton) { + let lastYearIndex = viewModel.lastYearIndex + chartSettings = (lastYearIndex, viewModel.lastMonthIndex(for: lastYearIndex)) + } + + @IBAction private func timeScaleChanged(_ sender: UISegmentedControl) { + guard let timeScale = TimeScale(rawValue: sender.selectedSegmentIndex) else { return } + currentTimeScale = timeScale + + switch currentTimeScale { + case .day: + let lastYearIndex = viewModel.lastYearIndex + chartSettings = (lastYearIndex, viewModel.lastMonthIndex(for: lastYearIndex)) + break + case .month: + let lastYearIndex = viewModel.lastYearIndex + chartSettings = (lastYearIndex, 0) + break + case .year: + chartSettings = (0, 0) + break + } + } +} + +// MARK: - IAxisValueFormatter +extension ServingsHistoryViewController: IAxisValueFormatter { + + func stringForValue(_ value: Double, axis: AxisBase?) -> String { + let labels: [String] + if currentTimeScale == .day { + labels = viewModel.datesLabels(yearIndex: chartSettings.year, monthIndex: chartSettings.month) + } else if currentTimeScale == .month { + labels = viewModel.monthsLabels(yearIndex: chartSettings.year) + } else { + labels = viewModel.fullDataLabels() + } + let index = Int(value) + guard index < labels.count else { return "" } + return labels[index] + } +} diff --git a/DailyDozen/DailyDozen/ServingsHistory/Models/Report.swift b/DailyDozen/DailyDozen/ServingsHistory/Models/Report.swift new file mode 100644 index 00000000..e5dac1f1 --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/Models/Report.swift @@ -0,0 +1,104 @@ +// +// Report.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 01.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation +import RealmSwift + +struct DailyReport { + var statesCount = 0 + var date: Date + + init(doze: Doze) { + var statesCount = 0 + for item in doze.items { + let selectedStates = item.states.filter { $0 } + statesCount += selectedStates.count + } + self.statesCount = statesCount + date = doze.date + } +} + +struct MonthReport { + var daily = [DailyReport]() + var month: String + var statesCount = 0 + + init(daily: [DailyReport], month: String) { + self.daily = daily + self.month = month + + statesCount = daily.reduce(0) { $0 + $1.statesCount } + + } +} + +struct YearlyReport { + var months = [MonthReport]() + var year: Int + var statesCount = 0 + + func monthReport(for index: Int) -> MonthReport { + return months[index] + } + + init(months: [MonthReport], year: Int) { + self.months = months + self.year = year + statesCount = months.reduce(0) { $0 + $1.statesCount } + } +} + +struct Report { + var data = [YearlyReport]() + + init(_ results: [Doze]) { + let dailyReports = results.map { DailyReport(doze: $0) } + var monthReports = [MonthReport]() + + guard var month = dailyReports.first?.date.monthName else { return } + + var reportsInMonth = [DailyReport]() + + dailyReports.forEach { report in + if report.date.monthName == month { + reportsInMonth.append(report) + month = report.date.monthName + } else { + let monthReport = MonthReport(daily: reportsInMonth, month: month) + monthReports.append(monthReport) + reportsInMonth.removeAll() + reportsInMonth.append(report) + month = report.date.monthName + } + } + monthReports.append(MonthReport(daily: reportsInMonth, month: month)) + + guard var year = monthReports.first?.daily.first?.date.year else { return } + + var reportsInYear = [MonthReport]() + + monthReports.forEach { report in + if report.daily.first!.date.year == year { + reportsInYear.append(report) + year = report.daily.first!.date.year + } else { + let yearlyReport = YearlyReport(months: reportsInYear, year: year) + data.append(yearlyReport) + reportsInYear.removeAll() + reportsInYear.append(report) + year = report.daily.first!.date.year + } + } + data.append(YearlyReport(months: reportsInYear, year: year)) + } + + func yearlyReport(for index: Int) -> YearlyReport { + return data[index] + } +} diff --git a/DailyDozen/DailyDozen/ServingsHistory/Storyboards/ServingsHistory.storyboard b/DailyDozen/DailyDozen/ServingsHistory/Storyboards/ServingsHistory.storyboard new file mode 100644 index 00000000..a21657ca --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/Storyboards/ServingsHistory.storyboard @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + HelveticaNeue-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DailyDozen/DailyDozen/ServingsHistory/ViewModels/ServingsHistoryViewModel.swift b/DailyDozen/DailyDozen/ServingsHistory/ViewModels/ServingsHistoryViewModel.swift new file mode 100644 index 00000000..8504c55b --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/ViewModels/ServingsHistoryViewModel.swift @@ -0,0 +1,78 @@ +// +// ServingsHistoryViewModel.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import Foundation +import RealmSwift + +struct ServingsHistoryViewModel { + + // MARK: - Properties + private let report: Report + + var lastYearIndex: Int { + return report.data.count - 1 + } + + // MARK: - Inits + init(_ results: Results) { + report = Report(Array(results)) + } + + // MARK: - Methods + func lastMonthIndex(for yearIndex: Int) -> Int { + return report.yearlyReport(for: yearIndex).months.count - 1 + } + + func monthData(yearIndex: Int, monthIndex: Int) -> (month: String, map: [Int]) { + let monthReport = report + .yearlyReport(for: yearIndex) + .monthReport(for: monthIndex) + + let month = monthReport.month + let map = monthReport.daily.map { $0.statesCount } + return (month, map) + } + + func yearlyData(yearIndex: Int) -> (year: String, map: [Int]) { + let yearlyReport = report.yearlyReport(for: yearIndex) + let year = String(yearlyReport.year) + let map = yearlyReport.months.map { $0.statesCount } + return (year, map) + } + + func fullDataMap() -> [Int] { + return report + .data + .map { $0.statesCount } + } + + func yearName(yearIndex: Int) -> String { + return String(report.yearlyReport(for: yearIndex).year) + } + + func datesLabels(yearIndex: Int, monthIndex: Int) -> [String] { + return report + .yearlyReport(for: yearIndex) + .monthReport(for: monthIndex) + .daily + .map { "\($0.date.day)" } + } + + func monthsLabels(yearIndex: Int) -> [String] { + return report + .yearlyReport(for: yearIndex) + .months + .map { $0.month } + } + + func fullDataLabels() -> [String] { + return report + .data + .map { String($0.year) } + } +} diff --git a/DailyDozen/DailyDozen/ServingsHistory/Views/ChartView.swift b/DailyDozen/DailyDozen/ServingsHistory/Views/ChartView.swift new file mode 100644 index 00000000..2227e886 --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/Views/ChartView.swift @@ -0,0 +1,110 @@ +// +// ChartView.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit +import Charts + +class ChartView: CombinedChartView { + + override func awakeFromNib() { + super.awakeFromNib() + + chartDescription?.enabled = false + drawBarShadowEnabled = false + highlightFullBarEnabled = false + setScaleEnabled(false) + dragYEnabled = false + + drawOrder = [DrawOrder.bar.rawValue, + DrawOrder.line.rawValue] + + legend.wordWrapEnabled = true + legend.horizontalAlignment = .center + legend.verticalAlignment = .bottom + legend.orientation = .horizontal + legend.drawInside = false + + rightAxis.axisMinimum = 0 + rightAxis.labelTextColor = UIColor.darkBlueColor + rightAxis.labelFont = UIFont.helevetica.withSize(12) + + leftAxis.axisMinimum = 0 + leftAxis.labelTextColor = UIColor.darkBlueColor + leftAxis.labelFont = UIFont.helevetica.withSize(12) + + xAxis.labelPosition = .bothSided + xAxis.granularity = 1 + xAxis.labelTextColor = .darkBlueColor + xAxis.labelFont = UIFont.helevetica.withSize(12) + } + + func configure(with map: [Int], for scale: TimeScale) { + let data = CombinedChartData() + if scale == .day { + data.barData = generateBarData(for: map) + } else { + let lineMap = map.map { Double($0) } + data.lineData = generateLineData(for: lineMap) + } + + xAxis.axisMaximum = data.xMax + 0.5 + xAxis.axisMinimum = data.xMin - 0.5 + + self.data = data + + setVisibleXRange(minXRange: 3, maxXRange: 7) + moveViewToX(Double(map.count)) + + self.data?.notifyDataChanged() + notifyDataSetChanged() + setNeedsDisplay() + } + + private func generateBarData(for map: [Int]) -> BarChartData { + + var entries = [BarChartDataEntry]() + + for (index, value) in map.enumerated() { + entries.append(BarChartDataEntry(x: Double(index), y: Double(value))) + } + + let set = BarChartDataSet(values: entries, label: "Servings") + set.setColor(UIColor.greenColor) + set.valueTextColor = UIColor.greenColor + set.valueFont = UIFont.helveticaBold.withSize(12) + set.axisDependency = .left + + let data = BarChartData(dataSet: set) + + return data + } + + private func generateLineData(for map: [Double]) -> LineChartData { + + var entries = [ChartDataEntry]() + + for (index, value) in map.enumerated() { + entries.append(ChartDataEntry(x: Double(index), y: value)) + } + + let set = LineChartDataSet(values: entries, label: "Servings") + set.setColor(UIColor.greenColor) + set.lineWidth = 2.5 + set.setCircleColor(UIColor.greenColor) + set.circleRadius = 5 + set.circleHoleRadius = 2.5 + set.fillColor = UIColor.white + set.mode = .cubicBezier + set.drawValuesEnabled = true + set.valueFont = UIFont.helveticaBold.withSize(12) + set.valueTextColor = UIColor.greenColor + set.axisDependency = .left + + return LineChartData(dataSet: set) + } +} diff --git a/DailyDozen/DailyDozen/ServingsHistory/Views/ControlPanel.swift b/DailyDozen/DailyDozen/ServingsHistory/Views/ControlPanel.swift new file mode 100644 index 00000000..d66d4d2c --- /dev/null +++ b/DailyDozen/DailyDozen/ServingsHistory/Views/ControlPanel.swift @@ -0,0 +1,43 @@ +// +// ControlPanel.swift +// DailyDozen +// +// Created by Konstantin Khokhlov on 07.12.2017. +// Copyright © 2017 Nutritionfacts.org. All rights reserved. +// + +import UIKit + +class ControlPanel: UIStackView { + + // MARK: - Outlets + @IBOutlet private weak var toFirstButton: RoundedButton! + @IBOutlet private weak var toPreviousButton: RoundedButton! + @IBOutlet private weak var toNextButton: RoundedButton! + @IBOutlet private weak var toLastButton: RoundedButton! + + @IBOutlet private weak var monthLabel: UILabel! { + didSet { + monthLabel.isHidden = monthLabel.text == nil + } + } + @IBOutlet private weak var yearLabel: UILabel! { + didSet { + yearLabel.isHidden = yearLabel.text == nil + } + } + + // MARK: - Methods + func configure(canSwitch: (left: Bool, right: Bool)) { + toFirstButton.isEnabled = canSwitch.left + toPreviousButton.isEnabled = canSwitch.left + toNextButton.isEnabled = canSwitch.right + toLastButton.isEnabled = canSwitch.right + } + + func setLabels(month: String? = nil, year: String? = nil) { + monthLabel.text = month + yearLabel.text = year + } + +} diff --git a/DailyDozen/Podfile b/DailyDozen/Podfile new file mode 100644 index 00000000..7d7fc241 --- /dev/null +++ b/DailyDozen/Podfile @@ -0,0 +1,17 @@ +# Uncomment the next line to define a global platform for your project + platform :ios, '10.0' + +target 'DailyDozen' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + inhibit_all_warnings! + + # Pods for DailyDozen + pod 'UICheckbox.Swift', '~> 1.0' + pod 'RealmSwift', '~> 3.0' + pod 'SimpleAnimation', '~> 0.3' + pod 'FSCalendar', '~> 2.7' + pod 'Charts', '~> 3.0' + pod 'ActiveLabel', :git => 'https://github.com/optonaut/ActiveLabel.swift.git', :tag => '0.8.0' + +end diff --git a/DailyDozen/Podfile.lock b/DailyDozen/Podfile.lock new file mode 100644 index 00000000..02915f46 --- /dev/null +++ b/DailyDozen/Podfile.lock @@ -0,0 +1,44 @@ +PODS: + - ActiveLabel (0.7.1) + - Charts (3.0.5): + - Charts/Core (= 3.0.5) + - Charts/Core (3.0.5) + - FSCalendar (2.7.9) + - Realm (3.1.0): + - Realm/Headers (= 3.1.0) + - Realm/Headers (3.1.0) + - RealmSwift (3.1.0): + - Realm (= 3.1.0) + - SimpleAnimation (0.4.0) + - UICheckbox.Swift (1.0.0) + +DEPENDENCIES: + - ActiveLabel (from `https://github.com/optonaut/ActiveLabel.swift.git`, tag `0.8.0`) + - Charts (~> 3.0) + - FSCalendar (~> 2.7) + - RealmSwift (~> 3.0) + - SimpleAnimation (~> 0.3) + - UICheckbox.Swift (~> 1.0) + +EXTERNAL SOURCES: + ActiveLabel: + :git: https://github.com/optonaut/ActiveLabel.swift.git + :tag: 0.8.0 + +CHECKOUT OPTIONS: + ActiveLabel: + :git: https://github.com/optonaut/ActiveLabel.swift.git + :tag: 0.8.0 + +SPEC CHECKSUMS: + ActiveLabel: faa96b5f50507770536a3e48a4cf291ee88fb7db + Charts: 45cdc985b764c838b35ee47eb1365d15735f6d1a + FSCalendar: a04b09f16f811bc92e82f3cf852a15225233b9d5 + Realm: 09513d7c054678d65cd02ce09871f805b0d758ac + RealmSwift: e868fee9b10e5490ad94b6b6ecd9027944da1824 + SimpleAnimation: a3473da5421c65100d68f98a44fc853b81091249 + UICheckbox.Swift: 85715689206fac5bae38b308f19c2a3e00442c3b + +PODFILE CHECKSUM: 5caabf47232433366e463a15b673f71f1ab71b7b + +COCOAPODS: 1.3.1 diff --git a/README.md b/README.md index 98038aea..3852fc9c 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,4 @@ Updates [donate]: https://nutritionfacts.org/donate "Donate to NutritionFacts.org" [book]: http://nutritionfacts.org/book "How Not to Die" [christirichards]: http://github.com/christirichards "Christi Richards on GitHub" -[laurenhacker]: http://github.com/lahacker "Lauren Hacker on Github" \ No newline at end of file +[laurenhacker]: http://github.com/lahacker "Lauren Hacker on Github"