Browse Source

Implement authentication

master
Dwayne Harris 6 years ago
parent
commit
5445354d61
  1. 12
      elpha-ios.xcodeproj/project.pbxproj
  2. 98
      elpha-ios/APIRequest.swift
  3. 70
      elpha-ios/AppDelegate.swift
  4. 99
      elpha-ios/AuthenticateViewController.swift
  5. 43
      elpha-ios/AuthenticationManager.swift
  6. 43
      elpha-ios/CoreDataManager.swift
  7. 4
      elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents
  8. 59
      elpha-ios/InstancesDataManager.swift
  9. 15
      elpha-ios/InstancesTableViewController.swift
  10. 1
      elpha-ios/MainTabBarController.swift

12
elpha-ios.xcodeproj/project.pbxproj

@ -10,7 +10,6 @@
157405A82150588A00EEAAEB /* InstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157405A72150588A00EEAAEB /* InstanceViewController.swift */; };
157405B12151A5DA00EEAAEB /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 157405AF2151A5DA00EEAAEB /* README.md */; };
157405B42151A93E00EEAAEB /* InstancesDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157405B32151A93E00EEAAEB /* InstancesDataManager.swift */; };
157405B621534B2C00EEAAEB /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157405B521534B2C00EEAAEB /* APIRequest.swift */; };
157405D1215890D700EEAAEB /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 157405C3215890BC00EEAAEB /* Alamofire.framework */; };
159048AF214F5015004F4014 /* InstancesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159048AE214F5015004F4014 /* InstancesTableViewCell.swift */; };
15960E5B213145E100C38CE9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E5A213145E100C38CE9 /* AppDelegate.swift */; };
@ -30,7 +29,8 @@
15960E84213774FC00C38CE9 /* InstancesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E83213774FC00C38CE9 /* InstancesTableViewController.swift */; };
15A79B07215B3CD5007A326E /* MastodonKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15A79B02215B3CC5007A326E /* MastodonKit.framework */; };
15A79B20215B439A007A326E /* OAuthSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15A79B13215B438C007A326E /* OAuthSwift.framework */; };
EAE6F556215C5E1E0028D2C4 /* AlamofireImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1517EA842159D72200DE80D6 /* AlamofireImage.framework */; };
15A79B2E215C63B6007A326E /* AlamofireImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1517EA842159D72200DE80D6 /* AlamofireImage.framework */; };
15A79B43215EB959007A326E /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A79B42215EB959007A326E /* CoreDataManager.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -223,7 +223,6 @@
157405A72150588A00EEAAEB /* InstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceViewController.swift; sourceTree = "<group>"; };
157405AF2151A5DA00EEAAEB /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
157405B32151A93E00EEAAEB /* InstancesDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesDataManager.swift; sourceTree = "<group>"; };
157405B521534B2C00EEAAEB /* APIRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIRequest.swift; sourceTree = "<group>"; };
157405B7215890BC00EEAAEB /* Alamofire.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Alamofire.xcodeproj; path = Frameworks/Alamofire/Alamofire.xcodeproj; sourceTree = "<group>"; };
159048AE214F5015004F4014 /* InstancesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesTableViewCell.swift; sourceTree = "<group>"; };
15960E57213145E100C38CE9 /* elpha-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "elpha-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -245,6 +244,7 @@
15960E83213774FC00C38CE9 /* InstancesTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesTableViewController.swift; sourceTree = "<group>"; };
15A79AE7215B3CC5007A326E /* MastodonKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MastodonKit.xcodeproj; path = Frameworks/MastodonKit/MastodonKit.xcodeproj; sourceTree = "<group>"; };
15A79B08215B438C007A326E /* OAuthSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OAuthSwift.xcodeproj; path = Frameworks/OAuthSwift/OAuthSwift.xcodeproj; sourceTree = "<group>"; };
15A79B42215EB959007A326E /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -252,9 +252,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
15A79B2E215C63B6007A326E /* AlamofireImage.framework in Frameworks */,
15A79B20215B439A007A326E /* OAuthSwift.framework in Frameworks */,
15A79B07215B3CD5007A326E /* MastodonKit.framework in Frameworks */,
1517EA932159D73500DE80D6 /* AlamofireImage.framework in Frameworks */,
157405D1215890D700EEAAEB /* Alamofire.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -324,10 +324,10 @@
isa = PBXGroup;
children = (
15960E68213145E200C38CE9 /* Info.plist */,
157405B521534B2C00EEAAEB /* APIRequest.swift */,
15960E5A213145E100C38CE9 /* AppDelegate.swift */,
15960E7B213272CD00C38CE9 /* AuthenticationManager.swift */,
15960E7621322C6F00C38CE9 /* Configuration.swift */,
15A79B42215EB959007A326E /* CoreDataManager.swift */,
157405B32151A93E00EEAAEB /* InstancesDataManager.swift */,
15960E63213145E200C38CE9 /* Assets.xcassets */,
15960E6E21321FA500C38CE9 /* Elpha.xcdatamodeld */,
@ -677,8 +677,8 @@
15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */,
15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */,
157405A82150588A00EEAAEB /* InstanceViewController.swift in Sources */,
157405B621534B2C00EEAAEB /* APIRequest.swift in Sources */,
15960E7321322BC700C38CE9 /* KeychainItemAccessibility.swift in Sources */,
15A79B43215EB959007A326E /* CoreDataManager.swift in Sources */,
15960E822136668500C38CE9 /* TimelinesNavigationController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

98
elpha-ios/APIRequest.swift

@ -1,98 +0,0 @@
//
// APIRequest.swift
// elpha-ios
//
// Created by Dwayne Harris on 9/19/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import Foundation
extension URLRequest {
private func percentEscapeString(_ string: String) -> String {
var characterSet = CharacterSet.alphanumerics
characterSet.insert(charactersIn: "-._* ")
return string
.addingPercentEncoding(withAllowedCharacters: characterSet)!
.replacingOccurrences(of: " ", with: "+")
}
mutating func encodeParameters(parameters: [String: String]) {
httpMethod = "POST"
let parameterArray = parameters.map { (arg) -> String in
let (key, value) = arg
return "\(key)=\(self.percentEscapeString(value))"
}
httpBody = parameterArray.joined(separator: "&").data(using: .utf8)
}
}
class APIRequest {
static func get(url: URL, completion: @escaping ([String: Any]?, Error?) -> Void) {
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError())
return
}
do {
completion(try JSONSerialization.jsonObject(with: data) as? [String: Any], nil)
} catch {
completion(nil, error)
}
}
}
static func post(url: URL, data: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.encodeParameters(parameters: data)
URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError())
return
}
completion(try? JSONSerialization.jsonObject(with: data) as! [String: Any], nil)
}
}
static func handleOAuthResponse(url: URL) throws {
}
static func registerApp(instance: URL, completion: @escaping ([String: Any]?, Error?) -> Void) {
let data = [
"client_name": Config.clientName,
"redirect_uris": "elpha:authenticate",
"scopes": "read write follow",
"website": Config.clientWebsite
]
APIRequest.post(url: instance.appendingPathComponent("api/v1/apps"), data: data) { data, error in
guard error == nil else {
completion(nil, error)
return
}
completion(data, nil)
}
}
}

70
elpha-ios/AppDelegate.swift

@ -12,12 +12,9 @@ import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
@ -28,72 +25,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
// 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: "Elpha")
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)")
}
}
}
}

99
elpha-ios/AuthenticateViewController.swift

@ -15,6 +15,8 @@ class AuthenticateViewController: UIViewController {
@IBOutlet var signInButton: UIButton!
@IBOutlet var instanceTextField: UITextField!
var oauthswift: OAuthSwift?
override func viewDidLoad() {
super.viewDidLoad()
@ -27,18 +29,52 @@ class AuthenticateViewController: UIViewController {
}
func saveApp(id: String, clientID: String, clientSecret: String, url: String) -> AppMO {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let app = AppMO(context: context)
let app = AppMO(context: CoreDataManager.shared.getContext())
app.id = id
app.clientID = clientID
app.clientSecret = clientSecret
app.url = url
CoreDataManager.shared.saveContext()
return app
}
func upsertAccount(_ remoteAccount: Account) -> AccountMO? {
func saveAccount(_ account: AccountMO) -> AccountMO? {
account.id = remoteAccount.id
account.username = remoteAccount.username
account.acct = remoteAccount.acct
account.displayName = remoteAccount.displayName
account.note = remoteAccount.note
account.url = remoteAccount.url
account.avatarURL = URL(string: remoteAccount.avatar)
account.avatarStaticURL = URL(string: remoteAccount.avatarStatic)
account.headerURL = URL(string: remoteAccount.header)
account.headerStaticURL = URL(string: remoteAccount.headerStatic)
account.locked = remoteAccount.locked
account.createdAt = remoteAccount.createdAt
account.followersCount = Int32(remoteAccount.followersCount)
account.followingCount = Int32(remoteAccount.followingCount)
account.statusesCount = Int32(remoteAccount.statusesCount)
CoreDataManager.shared.saveContext()
return account
}
let context = CoreDataManager.shared.getContext()
let request = NSFetchRequest<AccountMO>(entityName: "Account")
request.predicate = NSPredicate(format: "acct == %@", remoteAccount.acct)
do {
try context.save()
return app
let results = try context.fetch(request)
if let account = results.first {
return saveAccount(account)
} else {
return saveAccount(AccountMO(context: context))
}
} catch {
fatalError("Couldn't save app")
print("\(error)")
return nil
}
}
@ -47,15 +83,17 @@ class AuthenticateViewController: UIViewController {
consumerKey: app.clientID!,
consumerSecret: app.clientSecret!,
authorizeUrl: "https://\(app.url!)/oauth/authorize",
responseType: "token"
accessTokenUrl: "https://\(app.url!)/oauth/token",
responseType: "code"
)
oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)
self.oauthswift = oauthswift
oauthswift.authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance
let handle = oauthswift.authorize(
let _ = oauthswift.authorize(
withCallbackURL: URL(string: "elpha://oauth")!,
scope: "read write follow",
state: "elpha",
state: NSUUID().uuidString,
success: { credential, _, _ in
let client = Client(
baseURL: "https://\(app.url!)",
@ -65,10 +103,11 @@ class AuthenticateViewController: UIViewController {
let request = Accounts.currentUser()
client.run(request) { result in
switch result {
case .success(let _):
print("")
case .failure(let _):
print("")
case .success(let value):
let account = self.upsertAccount(value.0)
let _ = AuthenticationManager.shared.saveSession(app: app, account: account!, token: credential.oauthToken)
case .failure(let error):
print("\(error)")
}
}
},
@ -96,18 +135,24 @@ class AuthenticateViewController: UIViewController {
url = regex.stringByReplacingMatches(in: url, options: [], range: NSMakeRange(0, url.count), withTemplate: "")
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
signInButton.isEnabled = false
defer {
signInButton.isEnabled = true
}
let request = NSFetchRequest<AppMO>(entityName: "App")
request.predicate = NSPredicate(format: "url == %@", url)
do {
let response = try context.fetch(request)
switch response.count {
case 0:
let response = try CoreDataManager.shared.getContext().fetch(request)
if let app = response.first {
self.authorize(app: app)
self.dismiss(animated: true)
} else {
let client = Client(baseURL: "https://\(url)")
let request = Clients.register(
clientName: Config.clientDisplayName,
redirectURI: "elpha://oauth",
scopes: [.read, .write, .follow],
website: Config.clientWebsite
)
@ -115,24 +160,20 @@ class AuthenticateViewController: UIViewController {
client.run(request) { result in
switch result {
case .success(let value):
let app = value.0
let appMO = self.saveApp(
id: app.id,
clientID: app.clientID,
clientSecret: app.clientSecret,
let remoteApp = value.0
let app = self.saveApp(
id: remoteApp.id,
clientID: remoteApp.clientID,
clientSecret: remoteApp.clientSecret,
url: url
)
self.authorize(app: appMO)
self.authorize(app: app)
self.dismiss(animated: true)
case .failure(let error):
print("\(error)")
}
}
case 1:
self.authorize(app: response.first!)
return
default:
fatalError("Duplicate App entities")
}
} catch {
print("\(error)")

43
elpha-ios/AuthenticationManager.swift

@ -15,14 +15,11 @@ class AuthenticationManager {
var sessionCount: Int {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<SessionMO>(entityName: "Session")
do {
return try context.count(for: request)
let request = NSFetchRequest<SessionMO>(entityName: "Session")
return try CoreDataManager.shared.getContext().count(for: request)
} catch {
print("Error counting Sessions")
print("\(error)")
print("Error counting Sessions \(error)")
return 0
}
}
@ -30,26 +27,44 @@ class AuthenticationManager {
var selectedSession: SessionMO? {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<SessionMO>(entityName: "Session")
request.predicate = NSPredicate(format: "selected = YES")
do {
let results = try context.fetch(request)
let results = try CoreDataManager.shared.getContext().fetch(request)
return results.first
} catch {
print("Error fetching Session")
print("\(error)")
print("Error fetching Session \(error)")
return nil
}
}
}
private init() {}
var selectedSessionToken: String? {
get {
guard let session = selectedSession, let account = session.account else {
return nil
}
return KeychainWrapper.standard.string(forKey: "token:\(String(describing: account.acct))")
}
}
func saveSession(app: AppMO, token: String) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveSession(app: AppMO, account: AccountMO, token: String) -> SessionMO? {
if let selectedSession = selectedSession {
selectedSession.selected = false
}
let session = SessionMO(context: CoreDataManager.shared.getContext())
session.app = app
session.account = account
session.order = 0
session.createdAt = Date()
session.selected = true
CoreDataManager.shared.saveContext()
KeychainWrapper.standard.set(token, forKey: "token:\(String(describing: account.acct))")
let session = SessionMO(context: context)
return session
}
}

43
elpha-ios/CoreDataManager.swift

@ -0,0 +1,43 @@
//
// CoreDataManager.swift
// elpha-ios
//
// Created by Dwayne Harris on 9/28/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import CoreData
import Foundation
public class CoreDataManager {
static let shared = CoreDataManager()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Elpha")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func getContext() -> NSManagedObjectContext {
return persistentContainer.viewContext
}
func saveContext() {
let context = getContext()
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}

4
elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents

@ -13,7 +13,7 @@
<attribute name="headerData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="headerStaticURL" optional="YES" attributeType="URI" syncable="YES"/>
<attribute name="headerURL" optional="YES" attributeType="URI" syncable="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="locked" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="moved" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="note" attributeType="String" syncable="YES"/>
@ -84,7 +84,7 @@
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="App" inverseName="sessions" inverseEntity="App" syncable="YES"/>
</entity>
<elements>
<element name="Account" positionX="-669.49609375" positionY="52.046875" width="128" height="345"/>
<element name="Account" positionX="-669.49609375" positionY="52.046875" width="128" height="30"/>
<element name="App" positionX="-244.4140625" positionY="307.203125" width="128" height="120"/>
<element name="ISCategory" positionX="196.8984375" positionY="498.03515625" width="128" height="75"/>
<element name="ISInstance" positionX="-18" positionY="153" width="128" height="435"/>

59
elpha-ios/InstancesDataManager.swift

@ -20,7 +20,7 @@ class InstancesDataManager {
var finished = false
func upsertLanguage(string: String) -> ISLanguageMO? {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let context = CoreDataManager.shared.getContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
let request = NSFetchRequest<ISLanguageMO>(entityName: "ISLanguage")
@ -28,19 +28,14 @@ class InstancesDataManager {
do {
let results: [ISLanguageMO] = try context.fetch(request)
switch results.count {
case 0:
if let language = results.first {
return language
} else {
let language = ISLanguageMO(context: context)
language.id = string
try context.save()
CoreDataManager.shared.saveContext()
return language
case 1:
return results.first
default:
print("Duplicate ISLanguage entity")
return nil
}
} catch {
print("\(error)")
@ -49,7 +44,7 @@ class InstancesDataManager {
}
func upsertCategory(string: String) -> ISCategoryMO? {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let context = CoreDataManager.shared.getContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
let request = NSFetchRequest<ISCategoryMO>(entityName: "ISCategory")
@ -57,19 +52,14 @@ class InstancesDataManager {
do {
let results: [ISCategoryMO] = try context.fetch(request)
switch results.count {
case 0:
if let category = results.first {
return category
} else {
let category = ISCategoryMO(context: context)
category.id = string
try context.save()
CoreDataManager.shared.saveContext()
return category
case 1:
return results.first
default:
print("Duplicate ISCategory entity")
return nil
}
} catch {
print("\(error)")
@ -77,7 +67,7 @@ class InstancesDataManager {
}
}
func setAttributes(on i: ISInstanceMO, attributes: [String: Any]) {
func setAttributes(on i: ISInstanceMO, with attributes: [String: Any]) {
guard let id = attributes["id"] as? String, let name = attributes["name"] as? String else {
print("Error")
return
@ -136,12 +126,11 @@ class InstancesDataManager {
}
func clearInstances() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ISInstance")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
do {
try context.execute(deleteRequest)
try CoreDataManager.shared.getContext().execute(deleteRequest)
} catch {
print("\(error)")
}
@ -168,16 +157,14 @@ class InstancesDataManager {
}
let requestURL = "\(Config.instancesServiceUrl)\(Config.instancesServiceEndpoint)?\(params.joined(separator: "&"))"
print("loading instances: requestURL: \(requestURL)")
let headers: HTTPHeaders = ["Authorization": "Bearer \(Config.instancesServiceSecret)"]
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Config.instancesServiceSecret)",
]
print("loading instances: requestURL: \(requestURL)")
Alamofire.request(requestURL, headers: headers).validate().responseJSON { response in
switch response.result {
case .success:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let context = CoreDataManager.shared.getContext()
guard let result = response.result.value as? [String: Any],
let pagination = result["pagination"] as? [String: Any],
@ -200,22 +187,16 @@ class InstancesDataManager {
request.predicate = NSPredicate(format: "id == %@", id)
let results: [ISInstanceMO] = try context.fetch(request)
switch results.count {
case 0:
if let i = results.first {
self.setAttributes(on: i, with: instance)
} else {
let i = ISInstanceMO(context: context)
self.setAttributes(on: i, attributes: instance)
case 1:
if let i = results.first {
self.setAttributes(on: i, attributes: instance)
}
default:
print("Duplicate Instance entity")
self.setAttributes(on: i, with: instance)
}
}
}
try context.save()
CoreDataManager.shared.saveContext()
completion(nil)
} catch {
completion(error)

15
elpha-ios/InstancesTableViewController.swift

@ -29,10 +29,8 @@ class InstancesTableViewController: UITableViewController, UIViewControllerPrevi
var instanceCount: Int {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
return try context.count(for: self.getInstancesRequest())
return try CoreDataManager.shared.getContext().count(for: self.getInstancesRequest())
} catch {
return 0
}
@ -59,9 +57,8 @@ class InstancesTableViewController: UITableViewController, UIViewControllerPrevi
@objc func reloadInstances() {
loading = true
InstancesDataManager.shared.clearInstances()
DispatchQueue.main.async {
InstancesDataManager.shared.clearInstances()
self.tableView.reloadData()
}
@ -103,10 +100,8 @@ class InstancesTableViewController: UITableViewController, UIViewControllerPrevi
}
func getInstance(for indexPath: IndexPath) -> ISInstanceMO? {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
let instances = try context.fetch(self.getInstancesRequest())
let instances = try CoreDataManager.shared.getContext().fetch(self.getInstancesRequest())
return instances[indexPath.row]
} catch {
return nil
@ -122,14 +117,12 @@ class InstancesTableViewController: UITableViewController, UIViewControllerPrevi
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
guard let cell = tableView.dequeueReusableCell(withIdentifier: "InstancesTableViewCell", for: indexPath) as? InstancesTableViewCell else {
fatalError("Unable to find reusable cell")
}
do {
let instances = try context.fetch(self.getInstancesRequest())
let instances = try CoreDataManager.shared.getContext().fetch(self.getInstancesRequest())
let instance = instances[indexPath.row]
cell.instanceNameLabel.text = instance.name

1
elpha-ios/MainTabBarController.swift

@ -13,7 +13,6 @@ class MainTabBarController: UITabBarController {
super.viewDidAppear(animated)
if AuthenticationManager.shared.sessionCount == 0 {
print("Unauthenticated")
performSegue(withIdentifier: "AuthenticateSegue", sender: self)
return
}

Loading…
Cancel
Save