-
Notifications
You must be signed in to change notification settings - Fork 50
[team-03][Elly, Zeke] JSON 데이터 받아와서 Parsing, Diffable 적용 #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: team-03
Are you sure you want to change the base?
Changes from all commits
1942aca
15f0226
e5fe0f4
c220daa
b6ca4b2
9edf3ab
e60becb
489c7a6
0e8dab8
e152477
24b2337
f41512e
1017457
ac5f739
8e4319e
d5851e6
8004a94
9b87b1f
085ae09
7b0e4b6
b9afd5c
47be555
c3ebdb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| ### Xcode ### | ||
| # Xcode | ||
| # | ||
| # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore | ||
|
|
||
| ## User settings | ||
| xcuserdata/ | ||
|
|
||
| ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) | ||
| *.xcscmblueprint | ||
| *.xccheckout | ||
|
|
||
| ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) | ||
| build/ | ||
| DerivedData/ | ||
| *.moved-aside | ||
| *.pbxuser | ||
| !default.pbxuser | ||
| *.mode1v3 | ||
| !default.mode1v3 | ||
| *.mode2v3 | ||
| !default.mode2v3 | ||
| *.perspectivev3 | ||
| !default.perspectivev3 | ||
|
|
||
| ## Gcc Patch | ||
| /*.gcno | ||
|
|
||
| ### Xcode Patch ### | ||
| *.xcodeproj/* | ||
| !*.xcodeproj/project.pbxproj | ||
| !*.xcodeproj/xcshareddata/ | ||
| !*.xcworkspace/contents.xcworkspacedata | ||
| **/xcshareddata/WorkspaceSettings.xcsettings | ||
|
|
||
| # End of https://www.toptal.com/developers/gitignore/api/xcode |
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| <plist version="1.0"> | ||
| <dict> | ||
| <key>IDEDidComputeMac32BitWarning</key> | ||
| <true/> | ||
| </dict> | ||
| </plist> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| // | ||
| // AppDelegate.swift | ||
| // SideDish | ||
| // | ||
| // Created by 양준혁 on 2021/04/19. | ||
| // | ||
|
|
||
| import UIKit | ||
| import CoreData | ||
|
|
||
| @main | ||
| class AppDelegate: UIResponder, UIApplicationDelegate { | ||
|
|
||
|
|
||
|
|
||
| func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | ||
| // Override point for customization after application launch. | ||
| return true | ||
| } | ||
|
|
||
| // MARK: UISceneSession Lifecycle | ||
|
|
||
| func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { | ||
| // Called when a new scene session is being created. | ||
| // Use this method to select a configuration to create the new scene with. | ||
| return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) | ||
| } | ||
|
|
||
| func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { | ||
| // Called when the user discards a scene session. | ||
| // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. | ||
| // Use this method to release any resources that were specific to the discarded scenes, as they will not return. | ||
| } | ||
|
|
||
| // MARK: - Core Data stack | ||
|
|
||
| lazy var persistentContainer: NSPersistentContainer = { | ||
| /* | ||
| The persistent container for the application. This implementation | ||
| creates and returns a container, having loaded the store for the | ||
| application to it. This property is optional since there are legitimate | ||
| error conditions that could cause the creation of the store to fail. | ||
| */ | ||
| let container = NSPersistentContainer(name: "SideDish") | ||
| container.loadPersistentStores(completionHandler: { (storeDescription, error) in | ||
| if let error = error as NSError? { | ||
| // Replace this implementation with code to handle the error appropriately. | ||
| // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. | ||
|
|
||
| /* | ||
| Typical reasons for an error here include: | ||
| * The parent directory does not exist, cannot be created, or disallows writing. | ||
| * The persistent store is not accessible, due to permissions or data protection when the device is locked. | ||
| * The device is out of space. | ||
| * The store could not be migrated to the current model version. | ||
| Check the error message to determine what the actual problem was. | ||
| */ | ||
| fatalError("Unresolved error \(error), \(error.userInfo)") | ||
| } | ||
| }) | ||
| return container | ||
| }() | ||
|
|
||
| // MARK: - Core Data Saving support | ||
|
|
||
| func saveContext () { | ||
| let context = persistentContainer.viewContext | ||
| if context.hasChanges { | ||
| do { | ||
| try context.save() | ||
| } catch { | ||
| // Replace this implementation with code to handle the error appropriately. | ||
| // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. | ||
| let nserror = error as NSError | ||
| fatalError("Unresolved error \(nserror), \(nserror.userInfo)") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "colors" : [ | ||
| { | ||
| "idiom" : "universal" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "2x", | ||
| "size" : "20x20" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "3x", | ||
| "size" : "20x20" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "2x", | ||
| "size" : "29x29" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "3x", | ||
| "size" : "29x29" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "2x", | ||
| "size" : "40x40" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "3x", | ||
| "size" : "40x40" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "2x", | ||
| "size" : "60x60" | ||
| }, | ||
| { | ||
| "idiom" : "iphone", | ||
| "scale" : "3x", | ||
| "size" : "60x60" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "1x", | ||
| "size" : "20x20" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "2x", | ||
| "size" : "20x20" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "1x", | ||
| "size" : "29x29" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "2x", | ||
| "size" : "29x29" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "1x", | ||
| "size" : "40x40" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "2x", | ||
| "size" : "40x40" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "1x", | ||
| "size" : "76x76" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "2x", | ||
| "size" : "76x76" | ||
| }, | ||
| { | ||
| "idiom" : "ipad", | ||
| "scale" : "2x", | ||
| "size" : "83.5x83.5" | ||
| }, | ||
| { | ||
| "idiom" : "ios-marketing", | ||
| "scale" : "1x", | ||
| "size" : "1024x1024" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||
| <dependencies> | ||
| <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> | ||
| <capability name="Safe area layout guides" minToolsVersion="9.0"/> | ||
| <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
| </dependencies> | ||
| <scenes> | ||
| <!--View Controller--> | ||
| <scene sceneID="EHf-IW-A2E"> | ||
| <objects> | ||
| <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||
| <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||
| <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | ||
| <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
| <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> | ||
| <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | ||
| </view> | ||
| </viewController> | ||
| <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||
| </objects> | ||
| <point key="canvasLocation" x="53" y="375"/> | ||
| </scene> | ||
| </scenes> | ||
| </document> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| // | ||
| // ViewController.swift | ||
| // SideDish | ||
| // | ||
| // Created by 양준혁 on 2021/04/19. | ||
| // | ||
|
|
||
| import UIKit | ||
| import Toaster | ||
| import Combine | ||
|
|
||
| class ViewController: UIViewController { | ||
|
|
||
| // MARK: - Value Types | ||
| typealias DataSource = UICollectionViewDiffableDataSource<Section, Card> | ||
| typealias Snapshot = NSDiffableDataSourceSnapshot<Section, Card> | ||
|
|
||
| // MARK: - Properties | ||
| @IBOutlet weak var cardCollectionView: UICollectionView! | ||
|
|
||
| private var cancellables: Set<AnyCancellable> = [] | ||
| private var sections = Section.allSections | ||
| private lazy var dataSource = makeDataSource() | ||
|
|
||
| // MARK: - Life Cycles | ||
| override func viewDidLoad() { | ||
| super.viewDidLoad() | ||
| registerNIB() | ||
| fetchData() | ||
| makeSectionHeader() | ||
| } | ||
| } | ||
| // MARK: - Functions | ||
| extension ViewController { | ||
| func makeDataSource() -> DataSource { | ||
| let dataSource = DataSource( | ||
| collectionView: cardCollectionView, | ||
| cellProvider: { ( collectionview, indexPath, card) -> UICollectionViewCell? in | ||
| let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "cardCell", for: indexPath) as? CardCell | ||
| cell?.card = card | ||
| return cell | ||
| }) | ||
| return dataSource | ||
| } | ||
| func makeSectionHeader(){ | ||
| self.dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in | ||
| guard kind == UICollectionView.elementKindSectionHeader else { | ||
| return nil | ||
| } | ||
| let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section] | ||
| let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, | ||
| withReuseIdentifier: SectionHeaderReusableView.reuseIdentifier, | ||
| for: indexPath) as? SectionHeaderReusableView | ||
| headerView?.title.text = section.title | ||
| headerView?.configure() | ||
| return headerView | ||
| } | ||
| } | ||
| func fetchData(){ | ||
| self.sections.forEach{ | ||
| $0.$cards.receive(on: DispatchQueue.main) | ||
| .sink(receiveValue: {[weak self] _ in | ||
| self?.applySnapshot() | ||
| }) | ||
| .store(in: &cancellables) | ||
| } | ||
| self.sections[0].loadCards(type: .main) | ||
| self.sections[1].loadCards(type: .soup) | ||
| self.sections[2].loadCards(type: .side) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인덱스에 직접적으로 숫자를 넣는건 바람직하지 않습니다. section의 위치가 순서가 보장되어야 한다면,[.main, .soup, .side]로 동일한 순서를 만들어서 둘을 섞어보세요. |
||
| } | ||
| func applySnapshot(animatingDifferences : Bool = true){ | ||
| var snapshot = Snapshot() | ||
| snapshot.appendSections(sections) | ||
| sections.forEach{ section in | ||
| snapshot.appendItems(section.cards, toSection: section) | ||
| } | ||
| dataSource.apply(snapshot, animatingDifferences: animatingDifferences) | ||
| } | ||
| func registerNIB() { | ||
| let nibName = UINib(nibName: "CardCell", bundle: nil) | ||
| cardCollectionView.register(nibName, forCellWithReuseIdentifier: "cardCell") | ||
| } | ||
| } | ||
|
|
||
| extension ViewController: UICollectionViewDelegateFlowLayout { | ||
| func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | ||
| return CGSize(width: 343, height: 130) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클로져 내의 self 캡쳐링에 대한게 의도하신건지 확인이 필요할 것 같네요.