├── CoreDataRemote
├── Resources
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Info.plist
│ └── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
├── Data
│ ├── CoreDataStack.swift
│ ├── StarWars.xcdatamodeld
│ │ └── StarWars.xcdatamodel
│ │ │ └── contents
│ ├── Film.swift
│ └── DataProvider.swift
├── Repository
│ └── ApiRepository.swift
└── Controllers
│ └── FilmListViewController.swift
├── CoreDataRemote.xcodeproj
├── xcuserdata
│ └── alfianlosari.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── CoreDataRemote.xcscheme
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
└── README.md
/CoreDataRemote/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS App - Fetching Star Wars API Using Core Data Background Context
2 | The iOS app shows an example of fetching remote data from Star Wars API using Core Data Background ManagedObjectContext and merge the results to the View Context that uses NSFetchedResultsController to update the User Interface
3 |
4 | # Requirements
5 | 1. Xcode 9.4+
6 |
7 | ## Installation
8 | 1. Clone or Download the project
9 | 2. Open in Xcode
10 | 3. Build and Run the project
11 |
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CoreDataRemote.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 8B49D4C121588CDF00803884
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/CoreDataRemote/Resources/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 | var coreDataStack = CoreDataStack.shared
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 |
19 | let vc = (window?.rootViewController as? UINavigationController)?.topViewController as? FilmListViewController
20 | vc?.dataProvider = DataProvider(persistentContainer: coreDataStack.persistentContainer, repository: ApiRepository.shared)
21 |
22 | return true
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/CoreDataRemote/Data/CoreDataStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoreDataStack.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import CoreData
10 |
11 | class CoreDataStack {
12 |
13 | private init() {}
14 | static let shared = CoreDataStack()
15 |
16 | lazy var persistentContainer: NSPersistentContainer = {
17 | let container = NSPersistentContainer(name: "StarWars")
18 |
19 | container.loadPersistentStores(completionHandler: { (_, error) in
20 | guard let error = error as NSError? else { return }
21 | fatalError("Unresolved error: \(error), \(error.userInfo)")
22 | })
23 |
24 | container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
25 | container.viewContext.undoManager = nil
26 | container.viewContext.shouldDeleteInaccessibleFaults = true
27 |
28 | container.viewContext.automaticallyMergesChangesFromParent = true
29 |
30 | return container
31 | }()
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/CoreDataRemote/Data/StarWars.xcdatamodeld/StarWars.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CoreDataRemote/Data/Film.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Film.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import CoreData
10 |
11 | class Film: NSManagedObject {
12 |
13 | @NSManaged var director: String
14 | @NSManaged var episodeId: NSNumber
15 | @NSManaged var openingCrawl: String
16 | @NSManaged var producer: String
17 | @NSManaged var releaseDate: Date
18 | @NSManaged var title: String
19 |
20 | static let dateFormatter: DateFormatter = {
21 | let df = DateFormatter()
22 | df.dateFormat = "YYYY-MM-dd"
23 | return df
24 | }()
25 |
26 | func update(with jsonDictionary: [String: Any]) throws {
27 | guard let director = jsonDictionary["director"] as? String,
28 | let episodeId = jsonDictionary["episode_id"] as? Int,
29 | let openingCrawl = jsonDictionary["opening_crawl"] as? String,
30 | let producer = jsonDictionary["producer"] as? String,
31 | let releaseDate = jsonDictionary["release_date"] as? String,
32 | let title = jsonDictionary["title"] as? String
33 | else {
34 | throw NSError(domain: "", code: 100, userInfo: nil)
35 | }
36 |
37 | self.director = director
38 | self.episodeId = NSNumber(value: episodeId)
39 | self.openingCrawl = openingCrawl
40 | self.producer = producer
41 | self.releaseDate = Film.dateFormatter.date(from: releaseDate) ?? Date(timeIntervalSince1970: 0)
42 | self.title = title
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/CoreDataRemote/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/CoreDataRemote/Repository/ApiRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StarWarsRepository.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ApiRepository {
12 |
13 | private init() {}
14 | static let shared = ApiRepository()
15 |
16 | private let urlSession = URLSession.shared
17 | private let baseURL = URL(string: "https://swapi.co/api/")!
18 |
19 | func getFilms(completion: @escaping(_ filmsDict: [[String: Any]]?, _ error: Error?) -> ()) {
20 | let filmURL = baseURL.appendingPathComponent("films")
21 | urlSession.dataTask(with: filmURL) { (data, response, error) in
22 | if let error = error {
23 | completion(nil, error)
24 | return
25 | }
26 |
27 | guard let data = data else {
28 | let error = NSError(domain: dataErrorDomain, code: DataErrorCode.networkUnavailable.rawValue, userInfo: nil)
29 | completion(nil, error)
30 | return
31 | }
32 |
33 | do {
34 | let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
35 | guard let jsonDictionary = jsonObject as? [String: Any], let result = jsonDictionary["results"] as? [[String: Any]] else {
36 | throw NSError(domain: dataErrorDomain, code: DataErrorCode.wrongDataFormat.rawValue, userInfo: nil)
37 | }
38 | completion(result, nil)
39 | } catch {
40 | completion(nil, error)
41 | }
42 | }.resume()
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/CoreDataRemote/Resources/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/CoreDataRemote/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/CoreDataRemote/Controllers/FilmListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
11 |
12 | class FilmListViewController: UITableViewController {
13 |
14 | var dataProvider: DataProvider!
15 | lazy var fetchedResultsController: NSFetchedResultsController = {
16 | let fetchRequest = NSFetchRequest(entityName:"Film")
17 | fetchRequest.sortDescriptors = [NSSortDescriptor(key: "episodeId", ascending:true)]
18 |
19 | let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
20 | managedObjectContext: dataProvider.viewContext,
21 | sectionNameKeyPath: nil, cacheName: nil)
22 | controller.delegate = self
23 |
24 | do {
25 | try controller.performFetch()
26 | } catch {
27 | let nserror = error as NSError
28 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
29 | }
30 |
31 | return controller
32 | }()
33 |
34 |
35 | override func viewDidLoad() {
36 | super.viewDidLoad()
37 | dataProvider.fetchFilms { (error) in
38 | // Handle Error by displaying it in UI
39 | }
40 | }
41 |
42 | override func numberOfSections(in tableView: UITableView) -> Int {
43 | return fetchedResultsController.sections?.count ?? 0
44 | }
45 |
46 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
47 | return fetchedResultsController.sections?[section].numberOfObjects ?? 0
48 | }
49 |
50 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
51 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
52 | let film = fetchedResultsController.object(at: indexPath)
53 | cell.textLabel?.text = film.title
54 | cell.detailTextLabel?.text = film.director
55 | return cell
56 | }
57 |
58 | }
59 |
60 | extension FilmListViewController: NSFetchedResultsControllerDelegate {
61 |
62 | func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
63 | tableView.reloadData()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/CoreDataRemote.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/CoreDataRemote/Data/DataProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SyncCoordinator.swift
3 | // CoreDataRemote
4 | //
5 | // Created by Alfian Losari on 24/09/18.
6 | // Copyright © 2018 Alfian Losari. All rights reserved.
7 | //
8 |
9 | import CoreData
10 |
11 | let dataErrorDomain = "dataErrorDomain"
12 |
13 | enum DataErrorCode: NSInteger {
14 | case networkUnavailable = 101
15 | case wrongDataFormat = 102
16 | }
17 |
18 | class DataProvider {
19 |
20 | private let persistentContainer: NSPersistentContainer
21 | private let repository: ApiRepository
22 |
23 | var viewContext: NSManagedObjectContext {
24 | return persistentContainer.viewContext
25 | }
26 |
27 | init(persistentContainer: NSPersistentContainer, repository: ApiRepository) {
28 | self.persistentContainer = persistentContainer
29 | self.repository = repository
30 | }
31 |
32 | func fetchFilms(completion: @escaping(Error?) -> Void) {
33 | repository.getFilms() { jsonDictionary, error in
34 | if let error = error {
35 | completion(error)
36 | return
37 | }
38 |
39 | guard let jsonDictionary = jsonDictionary else {
40 | let error = NSError(domain: dataErrorDomain, code: DataErrorCode.wrongDataFormat.rawValue, userInfo: nil)
41 | completion(error)
42 | return
43 | }
44 |
45 | let taskContext = self.persistentContainer.newBackgroundContext()
46 | taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
47 | taskContext.undoManager = nil
48 |
49 | _ = self.syncFilms(jsonDictionary: jsonDictionary, taskContext: taskContext)
50 |
51 | completion(nil)
52 | }
53 | }
54 |
55 | private func syncFilms(jsonDictionary: [[String: Any]], taskContext: NSManagedObjectContext) -> Bool {
56 | var successfull = false
57 | taskContext.performAndWait {
58 | let matchingEpisodeRequest = NSFetchRequest(entityName: "Film")
59 | let episodeIds = jsonDictionary.map { $0["episode_id"] as? Int }.compactMap { $0 }
60 | matchingEpisodeRequest.predicate = NSPredicate(format: "episodeId in %@", argumentArray: [episodeIds])
61 |
62 | let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: matchingEpisodeRequest)
63 | batchDeleteRequest.resultType = .resultTypeObjectIDs
64 |
65 | // Execute the request to de batch delete and merge the changes to viewContext, which triggers the UI update
66 | do {
67 | let batchDeleteResult = try taskContext.execute(batchDeleteRequest) as? NSBatchDeleteResult
68 |
69 | if let deletedObjectIDs = batchDeleteResult?.result as? [NSManagedObjectID] {
70 | NSManagedObjectContext.mergeChanges(fromRemoteContextSave: [NSDeletedObjectsKey: deletedObjectIDs],
71 | into: [self.persistentContainer.viewContext])
72 | }
73 | } catch {
74 | print("Error: \(error)\nCould not batch delete existing records.")
75 | return
76 | }
77 |
78 | // Create new records.
79 | for filmDictionary in jsonDictionary {
80 |
81 | guard let film = NSEntityDescription.insertNewObject(forEntityName: "Film", into: taskContext) as? Film else {
82 | print("Error: Failed to create a new Film object!")
83 | return
84 | }
85 |
86 | do {
87 | try film.update(with: filmDictionary)
88 | } catch {
89 | print("Error: \(error)\nThe quake object will be deleted.")
90 | taskContext.delete(film)
91 | }
92 | }
93 |
94 | // Save all the changes just made and reset the taskContext to free the cache.
95 | if taskContext.hasChanges {
96 | do {
97 | try taskContext.save()
98 | } catch {
99 | print("Error: \(error)\nCould not save Core Data context.")
100 | }
101 | taskContext.reset() // Reset the context to clean up the cache and low the memory footprint.
102 | }
103 | successfull = true
104 | }
105 | return successfull
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/CoreDataRemote/Resources/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/CoreDataRemote.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8B49D4C621588CDF00803884 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4C521588CDF00803884 /* AppDelegate.swift */; };
11 | 8B49D4C821588CDF00803884 /* FilmListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4C721588CDF00803884 /* FilmListViewController.swift */; };
12 | 8B49D4CB21588CDF00803884 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B49D4C921588CDF00803884 /* Main.storyboard */; };
13 | 8B49D4CD21588CE000803884 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B49D4CC21588CE000803884 /* Assets.xcassets */; };
14 | 8B49D4D021588CE000803884 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B49D4CE21588CE000803884 /* LaunchScreen.storyboard */; };
15 | 8B49D4D8215892EC00803884 /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4D7215892EC00803884 /* CoreDataStack.swift */; };
16 | 8B49D4DA2158A23300803884 /* ApiRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4D92158A23300803884 /* ApiRepository.swift */; };
17 | 8B49D4DD2158A40B00803884 /* StarWars.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4DB2158A40B00803884 /* StarWars.xcdatamodeld */; };
18 | 8B49D4DF2158A57D00803884 /* Film.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4DE2158A57D00803884 /* Film.swift */; };
19 | 8B49D4E12158B20D00803884 /* DataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B49D4E02158B20D00803884 /* DataProvider.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | 8B49D4C221588CDF00803884 /* CoreDataRemote.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoreDataRemote.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | 8B49D4C521588CDF00803884 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
25 | 8B49D4C721588CDF00803884 /* FilmListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilmListViewController.swift; sourceTree = ""; };
26 | 8B49D4CA21588CDF00803884 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
27 | 8B49D4CC21588CE000803884 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
28 | 8B49D4CF21588CE000803884 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
29 | 8B49D4D121588CE000803884 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 8B49D4D7215892EC00803884 /* CoreDataStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; };
31 | 8B49D4D92158A23300803884 /* ApiRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiRepository.swift; sourceTree = ""; };
32 | 8B49D4DC2158A40B00803884 /* StarWars.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = StarWars.xcdatamodel; sourceTree = ""; };
33 | 8B49D4DE2158A57D00803884 /* Film.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Film.swift; sourceTree = ""; };
34 | 8B49D4E02158B20D00803884 /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 8B49D4BF21588CDF00803884 /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | 8B49D4B921588CDF00803884 = {
49 | isa = PBXGroup;
50 | children = (
51 | 8B49D4C421588CDF00803884 /* CoreDataRemote */,
52 | 8B49D4C321588CDF00803884 /* Products */,
53 | );
54 | sourceTree = "";
55 | };
56 | 8B49D4C321588CDF00803884 /* Products */ = {
57 | isa = PBXGroup;
58 | children = (
59 | 8B49D4C221588CDF00803884 /* CoreDataRemote.app */,
60 | );
61 | name = Products;
62 | sourceTree = "";
63 | };
64 | 8B49D4C421588CDF00803884 /* CoreDataRemote */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 8B72E3AE215BEBD90008CBBA /* Controllers */,
68 | 8B72E3AC215BEBA80008CBBA /* Data */,
69 | 8B72E3AD215BEBC10008CBBA /* Repository */,
70 | 8B72E3AF215BEC010008CBBA /* Resources */,
71 | );
72 | path = CoreDataRemote;
73 | sourceTree = "";
74 | };
75 | 8B72E3AC215BEBA80008CBBA /* Data */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 8B49D4D7215892EC00803884 /* CoreDataStack.swift */,
79 | 8B49D4DB2158A40B00803884 /* StarWars.xcdatamodeld */,
80 | 8B49D4E02158B20D00803884 /* DataProvider.swift */,
81 | 8B49D4DE2158A57D00803884 /* Film.swift */,
82 | );
83 | path = Data;
84 | sourceTree = "";
85 | };
86 | 8B72E3AD215BEBC10008CBBA /* Repository */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 8B49D4D92158A23300803884 /* ApiRepository.swift */,
90 | );
91 | path = Repository;
92 | sourceTree = "";
93 | };
94 | 8B72E3AE215BEBD90008CBBA /* Controllers */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 8B49D4C721588CDF00803884 /* FilmListViewController.swift */,
98 | );
99 | path = Controllers;
100 | sourceTree = "";
101 | };
102 | 8B72E3AF215BEC010008CBBA /* Resources */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 8B49D4C521588CDF00803884 /* AppDelegate.swift */,
106 | 8B49D4C921588CDF00803884 /* Main.storyboard */,
107 | 8B49D4CC21588CE000803884 /* Assets.xcassets */,
108 | 8B49D4CE21588CE000803884 /* LaunchScreen.storyboard */,
109 | 8B49D4D121588CE000803884 /* Info.plist */,
110 | );
111 | path = Resources;
112 | sourceTree = "";
113 | };
114 | /* End PBXGroup section */
115 |
116 | /* Begin PBXNativeTarget section */
117 | 8B49D4C121588CDF00803884 /* CoreDataRemote */ = {
118 | isa = PBXNativeTarget;
119 | buildConfigurationList = 8B49D4D421588CE000803884 /* Build configuration list for PBXNativeTarget "CoreDataRemote" */;
120 | buildPhases = (
121 | 8B49D4BE21588CDF00803884 /* Sources */,
122 | 8B49D4BF21588CDF00803884 /* Frameworks */,
123 | 8B49D4C021588CDF00803884 /* Resources */,
124 | );
125 | buildRules = (
126 | );
127 | dependencies = (
128 | );
129 | name = CoreDataRemote;
130 | productName = CoreDataRemote;
131 | productReference = 8B49D4C221588CDF00803884 /* CoreDataRemote.app */;
132 | productType = "com.apple.product-type.application";
133 | };
134 | /* End PBXNativeTarget section */
135 |
136 | /* Begin PBXProject section */
137 | 8B49D4BA21588CDF00803884 /* Project object */ = {
138 | isa = PBXProject;
139 | attributes = {
140 | LastSwiftUpdateCheck = 1000;
141 | LastUpgradeCheck = 1000;
142 | ORGANIZATIONNAME = "Alfian Losari";
143 | TargetAttributes = {
144 | 8B49D4C121588CDF00803884 = {
145 | CreatedOnToolsVersion = 10.0;
146 | };
147 | };
148 | };
149 | buildConfigurationList = 8B49D4BD21588CDF00803884 /* Build configuration list for PBXProject "CoreDataRemote" */;
150 | compatibilityVersion = "Xcode 9.3";
151 | developmentRegion = en;
152 | hasScannedForEncodings = 0;
153 | knownRegions = (
154 | en,
155 | Base,
156 | );
157 | mainGroup = 8B49D4B921588CDF00803884;
158 | productRefGroup = 8B49D4C321588CDF00803884 /* Products */;
159 | projectDirPath = "";
160 | projectRoot = "";
161 | targets = (
162 | 8B49D4C121588CDF00803884 /* CoreDataRemote */,
163 | );
164 | };
165 | /* End PBXProject section */
166 |
167 | /* Begin PBXResourcesBuildPhase section */
168 | 8B49D4C021588CDF00803884 /* Resources */ = {
169 | isa = PBXResourcesBuildPhase;
170 | buildActionMask = 2147483647;
171 | files = (
172 | 8B49D4D021588CE000803884 /* LaunchScreen.storyboard in Resources */,
173 | 8B49D4CD21588CE000803884 /* Assets.xcassets in Resources */,
174 | 8B49D4CB21588CDF00803884 /* Main.storyboard in Resources */,
175 | );
176 | runOnlyForDeploymentPostprocessing = 0;
177 | };
178 | /* End PBXResourcesBuildPhase section */
179 |
180 | /* Begin PBXSourcesBuildPhase section */
181 | 8B49D4BE21588CDF00803884 /* Sources */ = {
182 | isa = PBXSourcesBuildPhase;
183 | buildActionMask = 2147483647;
184 | files = (
185 | 8B49D4D8215892EC00803884 /* CoreDataStack.swift in Sources */,
186 | 8B49D4DF2158A57D00803884 /* Film.swift in Sources */,
187 | 8B49D4E12158B20D00803884 /* DataProvider.swift in Sources */,
188 | 8B49D4C821588CDF00803884 /* FilmListViewController.swift in Sources */,
189 | 8B49D4DD2158A40B00803884 /* StarWars.xcdatamodeld in Sources */,
190 | 8B49D4DA2158A23300803884 /* ApiRepository.swift in Sources */,
191 | 8B49D4C621588CDF00803884 /* AppDelegate.swift in Sources */,
192 | );
193 | runOnlyForDeploymentPostprocessing = 0;
194 | };
195 | /* End PBXSourcesBuildPhase section */
196 |
197 | /* Begin PBXVariantGroup section */
198 | 8B49D4C921588CDF00803884 /* Main.storyboard */ = {
199 | isa = PBXVariantGroup;
200 | children = (
201 | 8B49D4CA21588CDF00803884 /* Base */,
202 | );
203 | name = Main.storyboard;
204 | sourceTree = "";
205 | };
206 | 8B49D4CE21588CE000803884 /* LaunchScreen.storyboard */ = {
207 | isa = PBXVariantGroup;
208 | children = (
209 | 8B49D4CF21588CE000803884 /* Base */,
210 | );
211 | name = LaunchScreen.storyboard;
212 | sourceTree = "";
213 | };
214 | /* End PBXVariantGroup section */
215 |
216 | /* Begin XCBuildConfiguration section */
217 | 8B49D4D221588CE000803884 /* Debug */ = {
218 | isa = XCBuildConfiguration;
219 | buildSettings = {
220 | ALWAYS_SEARCH_USER_PATHS = NO;
221 | CLANG_ANALYZER_NONNULL = YES;
222 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
224 | CLANG_CXX_LIBRARY = "libc++";
225 | CLANG_ENABLE_MODULES = YES;
226 | CLANG_ENABLE_OBJC_ARC = YES;
227 | CLANG_ENABLE_OBJC_WEAK = YES;
228 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
229 | CLANG_WARN_BOOL_CONVERSION = YES;
230 | CLANG_WARN_COMMA = YES;
231 | CLANG_WARN_CONSTANT_CONVERSION = YES;
232 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
233 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
234 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
235 | CLANG_WARN_EMPTY_BODY = YES;
236 | CLANG_WARN_ENUM_CONVERSION = YES;
237 | CLANG_WARN_INFINITE_RECURSION = YES;
238 | CLANG_WARN_INT_CONVERSION = YES;
239 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
240 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
241 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
242 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
243 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
244 | CLANG_WARN_STRICT_PROTOTYPES = YES;
245 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
246 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
247 | CLANG_WARN_UNREACHABLE_CODE = YES;
248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
249 | CODE_SIGN_IDENTITY = "iPhone Developer";
250 | COPY_PHASE_STRIP = NO;
251 | DEBUG_INFORMATION_FORMAT = dwarf;
252 | ENABLE_STRICT_OBJC_MSGSEND = YES;
253 | ENABLE_TESTABILITY = YES;
254 | GCC_C_LANGUAGE_STANDARD = gnu11;
255 | GCC_DYNAMIC_NO_PIC = NO;
256 | GCC_NO_COMMON_BLOCKS = YES;
257 | GCC_OPTIMIZATION_LEVEL = 0;
258 | GCC_PREPROCESSOR_DEFINITIONS = (
259 | "DEBUG=1",
260 | "$(inherited)",
261 | );
262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
264 | GCC_WARN_UNDECLARED_SELECTOR = YES;
265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
266 | GCC_WARN_UNUSED_FUNCTION = YES;
267 | GCC_WARN_UNUSED_VARIABLE = YES;
268 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
269 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
270 | MTL_FAST_MATH = YES;
271 | ONLY_ACTIVE_ARCH = YES;
272 | SDKROOT = iphoneos;
273 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
274 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
275 | };
276 | name = Debug;
277 | };
278 | 8B49D4D321588CE000803884 /* Release */ = {
279 | isa = XCBuildConfiguration;
280 | buildSettings = {
281 | ALWAYS_SEARCH_USER_PATHS = NO;
282 | CLANG_ANALYZER_NONNULL = YES;
283 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
284 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
285 | CLANG_CXX_LIBRARY = "libc++";
286 | CLANG_ENABLE_MODULES = YES;
287 | CLANG_ENABLE_OBJC_ARC = YES;
288 | CLANG_ENABLE_OBJC_WEAK = YES;
289 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
290 | CLANG_WARN_BOOL_CONVERSION = YES;
291 | CLANG_WARN_COMMA = YES;
292 | CLANG_WARN_CONSTANT_CONVERSION = YES;
293 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
294 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
295 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
296 | CLANG_WARN_EMPTY_BODY = YES;
297 | CLANG_WARN_ENUM_CONVERSION = YES;
298 | CLANG_WARN_INFINITE_RECURSION = YES;
299 | CLANG_WARN_INT_CONVERSION = YES;
300 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
301 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
302 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
303 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
304 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
305 | CLANG_WARN_STRICT_PROTOTYPES = YES;
306 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
307 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
308 | CLANG_WARN_UNREACHABLE_CODE = YES;
309 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
310 | CODE_SIGN_IDENTITY = "iPhone Developer";
311 | COPY_PHASE_STRIP = NO;
312 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
313 | ENABLE_NS_ASSERTIONS = NO;
314 | ENABLE_STRICT_OBJC_MSGSEND = YES;
315 | GCC_C_LANGUAGE_STANDARD = gnu11;
316 | GCC_NO_COMMON_BLOCKS = YES;
317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
319 | GCC_WARN_UNDECLARED_SELECTOR = YES;
320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
321 | GCC_WARN_UNUSED_FUNCTION = YES;
322 | GCC_WARN_UNUSED_VARIABLE = YES;
323 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
324 | MTL_ENABLE_DEBUG_INFO = NO;
325 | MTL_FAST_MATH = YES;
326 | SDKROOT = iphoneos;
327 | SWIFT_COMPILATION_MODE = wholemodule;
328 | SWIFT_OPTIMIZATION_LEVEL = "-O";
329 | VALIDATE_PRODUCT = YES;
330 | };
331 | name = Release;
332 | };
333 | 8B49D4D521588CE000803884 /* Debug */ = {
334 | isa = XCBuildConfiguration;
335 | buildSettings = {
336 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
337 | CODE_SIGN_STYLE = Automatic;
338 | DEVELOPMENT_TEAM = 5C2XD9H2JS;
339 | INFOPLIST_FILE = "$(SRCROOT)/CoreDataRemote/Resources/Info.plist";
340 | LD_RUNPATH_SEARCH_PATHS = (
341 | "$(inherited)",
342 | "@executable_path/Frameworks",
343 | );
344 | PRODUCT_BUNDLE_IDENTIFIER = com.alfianlosari.CoreDataRemote;
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | SWIFT_VERSION = 4.2;
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | };
349 | name = Debug;
350 | };
351 | 8B49D4D621588CE000803884 /* Release */ = {
352 | isa = XCBuildConfiguration;
353 | buildSettings = {
354 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
355 | CODE_SIGN_STYLE = Automatic;
356 | DEVELOPMENT_TEAM = 5C2XD9H2JS;
357 | INFOPLIST_FILE = "$(SRCROOT)/CoreDataRemote/Resources/Info.plist";
358 | LD_RUNPATH_SEARCH_PATHS = (
359 | "$(inherited)",
360 | "@executable_path/Frameworks",
361 | );
362 | PRODUCT_BUNDLE_IDENTIFIER = com.alfianlosari.CoreDataRemote;
363 | PRODUCT_NAME = "$(TARGET_NAME)";
364 | SWIFT_VERSION = 4.2;
365 | TARGETED_DEVICE_FAMILY = "1,2";
366 | };
367 | name = Release;
368 | };
369 | /* End XCBuildConfiguration section */
370 |
371 | /* Begin XCConfigurationList section */
372 | 8B49D4BD21588CDF00803884 /* Build configuration list for PBXProject "CoreDataRemote" */ = {
373 | isa = XCConfigurationList;
374 | buildConfigurations = (
375 | 8B49D4D221588CE000803884 /* Debug */,
376 | 8B49D4D321588CE000803884 /* Release */,
377 | );
378 | defaultConfigurationIsVisible = 0;
379 | defaultConfigurationName = Release;
380 | };
381 | 8B49D4D421588CE000803884 /* Build configuration list for PBXNativeTarget "CoreDataRemote" */ = {
382 | isa = XCConfigurationList;
383 | buildConfigurations = (
384 | 8B49D4D521588CE000803884 /* Debug */,
385 | 8B49D4D621588CE000803884 /* Release */,
386 | );
387 | defaultConfigurationIsVisible = 0;
388 | defaultConfigurationName = Release;
389 | };
390 | /* End XCConfigurationList section */
391 |
392 | /* Begin XCVersionGroup section */
393 | 8B49D4DB2158A40B00803884 /* StarWars.xcdatamodeld */ = {
394 | isa = XCVersionGroup;
395 | children = (
396 | 8B49D4DC2158A40B00803884 /* StarWars.xcdatamodel */,
397 | );
398 | currentVersion = 8B49D4DC2158A40B00803884 /* StarWars.xcdatamodel */;
399 | path = StarWars.xcdatamodeld;
400 | sourceTree = "";
401 | versionGroupType = wrapper.xcdatamodel;
402 | };
403 | /* End XCVersionGroup section */
404 | };
405 | rootObject = 8B49D4BA21588CDF00803884 /* Project object */;
406 | }
407 |
--------------------------------------------------------------------------------