Browse Source

Initial commit

master
Dwayne Harris 6 years ago
parent
commit
477d95b702
  1. 4
      .gitignore
  2. 2
      README.md
  3. 69
      elpha-ios.xcodeproj/project.pbxproj
  4. 48
      elpha-ios/AppDelegate.swift
  5. 15
      elpha-ios/AuthenticateViewController.swift
  6. 49
      elpha-ios/AuthenticationManager.swift
  7. 19
      elpha-ios/Base.lproj/LaunchScreen.storyboard
  8. 45
      elpha-ios/Base.lproj/Main.storyboard
  9. 13
      elpha-ios/Configuration.swift
  10. 41
      elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents
  11. 2
      elpha-ios/Info.plist
  12. 124
      elpha-ios/KeychainItemAccessibility.swift
  13. 445
      elpha-ios/KeychainWrapper.swift
  14. 25
      elpha-ios/MainTabBarController.swift

4
.gitignore

@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj

2
README.md

@ -0,0 +1,2 @@
# elpha-ios
Elpha iOS app.

69
elpha-ios.xcodeproj/project.pbxproj

@ -13,6 +13,13 @@
15960E62213145E100C38CE9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 15960E60213145E100C38CE9 /* Main.storyboard */; };
15960E64213145E200C38CE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 15960E63213145E200C38CE9 /* Assets.xcassets */; };
15960E67213145E200C38CE9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 15960E65213145E200C38CE9 /* LaunchScreen.storyboard */; };
15960E7021321FA500C38CE9 /* Elpha.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 15960E6E21321FA500C38CE9 /* Elpha.xcdatamodeld */; };
15960E7321322BC700C38CE9 /* KeychainItemAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7221322BC700C38CE9 /* KeychainItemAccessibility.swift */; };
15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7421322BF800C38CE9 /* KeychainWrapper.swift */; };
15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7621322C6F00C38CE9 /* Configuration.swift */; };
15960E7A2132387A00C38CE9 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E792132387A00C38CE9 /* MainTabBarController.swift */; };
15960E7C213272CD00C38CE9 /* AuthenticationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7B213272CD00C38CE9 /* AuthenticationManager.swift */; };
15960E7E21329FED00C38CE9 /* AuthenticateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -24,6 +31,13 @@
15960E63213145E200C38CE9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
15960E66213145E200C38CE9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
15960E68213145E200C38CE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
15960E6F21321FA500C38CE9 /* Elpha.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Elpha.xcdatamodel; sourceTree = "<group>"; };
15960E7221322BC700C38CE9 /* KeychainItemAccessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainItemAccessibility.swift; sourceTree = "<group>"; };
15960E7421322BF800C38CE9 /* KeychainWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainWrapper.swift; sourceTree = "<group>"; };
15960E7621322C6F00C38CE9 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
15960E792132387A00C38CE9 /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
15960E7B213272CD00C38CE9 /* AuthenticationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationManager.swift; sourceTree = "<group>"; };
15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticateViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -56,17 +70,40 @@
15960E59213145E100C38CE9 /* elpha-ios */ = {
isa = PBXGroup;
children = (
15960E68213145E200C38CE9 /* Info.plist */,
15960E5A213145E100C38CE9 /* AppDelegate.swift */,
15960E5C213145E100C38CE9 /* FirstViewController.swift */,
15960E5E213145E100C38CE9 /* SecondViewController.swift */,
15960E60213145E100C38CE9 /* Main.storyboard */,
15960E7B213272CD00C38CE9 /* AuthenticationManager.swift */,
15960E7621322C6F00C38CE9 /* Configuration.swift */,
15960E63213145E200C38CE9 /* Assets.xcassets */,
15960E6E21321FA500C38CE9 /* Elpha.xcdatamodeld */,
15960E7121322B9F00C38CE9 /* Keychain Helper */,
15960E65213145E200C38CE9 /* LaunchScreen.storyboard */,
15960E68213145E200C38CE9 /* Info.plist */,
15960E60213145E100C38CE9 /* Main.storyboard */,
15960E782132383600C38CE9 /* View Controllers */,
);
path = "elpha-ios";
sourceTree = "<group>";
};
15960E7121322B9F00C38CE9 /* Keychain Helper */ = {
isa = PBXGroup;
children = (
15960E7221322BC700C38CE9 /* KeychainItemAccessibility.swift */,
15960E7421322BF800C38CE9 /* KeychainWrapper.swift */,
);
name = "Keychain Helper";
sourceTree = "<group>";
};
15960E782132383600C38CE9 /* View Controllers */ = {
isa = PBXGroup;
children = (
15960E5C213145E100C38CE9 /* FirstViewController.swift */,
15960E5E213145E100C38CE9 /* SecondViewController.swift */,
15960E792132387A00C38CE9 /* MainTabBarController.swift */,
15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */,
);
name = "View Controllers";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -138,8 +175,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
15960E7021321FA500C38CE9 /* Elpha.xcdatamodeld in Sources */,
15960E5F213145E100C38CE9 /* SecondViewController.swift in Sources */,
15960E7A2132387A00C38CE9 /* MainTabBarController.swift in Sources */,
15960E7C213272CD00C38CE9 /* AuthenticationManager.swift in Sources */,
15960E7E21329FED00C38CE9 /* AuthenticateViewController.swift in Sources */,
15960E5B213145E100C38CE9 /* AppDelegate.swift in Sources */,
15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */,
15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */,
15960E7321322BC700C38CE9 /* KeychainItemAccessibility.swift in Sources */,
15960E5D213145E100C38CE9 /* FirstViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -294,7 +338,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "xyz.elpha.elpha-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@ -312,7 +356,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "xyz.elpha.elpha-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
@ -338,6 +382,19 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
15960E6E21321FA500C38CE9 /* Elpha.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
15960E6F21321FA500C38CE9 /* Elpha.xcdatamodel */,
);
currentVersion = 15960E6F21321FA500C38CE9 /* Elpha.xcdatamodel */;
path = Elpha.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = 15960E4F213145E100C38CE9 /* Project object */;
}

48
elpha-ios/AppDelegate.swift

@ -6,6 +6,7 @@
// Copyright © 2018 Elpha. All rights reserved.
//
import CoreData
import UIKit
@UIApplicationMain
@ -40,7 +41,50 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
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)")
}
}
}
}

15
elpha-ios/AuthenticateViewController.swift

@ -0,0 +1,15 @@
//
// AuthenticateViewController.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/26/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import UIKit
class AuthenticateViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}

49
elpha-ios/AuthenticationManager.swift

@ -0,0 +1,49 @@
//
// AuthenticationManager.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/25/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import CoreData
import Foundation
import UIKit
class AuthenticationManager {
static let shared = AuthenticationManager()
var sessionCount: Int {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<Session>(entityName: "Session")
do {
return try context.count(for: request)
} catch {
print("Error counting Sessions")
print("\(error)")
return 0
}
}
}
var selectedSession: Session? {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<Session>(entityName: "Session")
request.predicate = NSPredicate(format: "selected = YES")
do {
let results = try context.fetch(request)
return results.first
} catch {
print("Error fetching Session")
print("\(error)")
return nil
}
}
}
private init() {}
}

19
elpha-ios/Base.lproj/LaunchScreen.storyboard

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -13,6 +17,15 @@
<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"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Elpha" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pds-N4-1zg">
<rect key="frame" x="139" y="309" width="96" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="40"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>

45
elpha-ios/Base.lproj/Main.storyboard

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A278a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -9,18 +13,20 @@
<!--First-->
<scene sceneID="hNz-n2-bh7">
<objects>
<viewController id="9pv-A4-QxB" customClass="FirstViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="9pv-A4-QxB" customClass="FirstViewController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="tsR-hK-woN">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="First View" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="KQZ-1w-vlD">
<rect key="frame" x="112.5" y="312" width="150.5" height="43"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loaded by FirstViewController" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A5M-7J-77L">
<rect key="frame" x="90.5" y="363" width="194.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
@ -41,21 +47,47 @@
</objects>
<point key="canvasLocation" x="750" y="-320"/>
</scene>
<!--Authenticate View Controller-->
<scene sceneID="ti2-vn-Sq9">
<objects>
<viewController id="Bed-pj-wE9" customClass="AuthenticateViewController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="ITR-hY-wTb">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Authenticate" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OWL-Bn-9c1">
<rect key="frame" x="135" y="318" width="104" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="x4b-tk-8EQ"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="aNa-IN-5Hu" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-615" y="771"/>
</scene>
<!--Second-->
<scene sceneID="wg7-f3-ORb">
<objects>
<viewController id="8rJ-Kc-sve" customClass="SecondViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="8rJ-Kc-sve" customClass="SecondViewController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="QS5-Rx-YEW">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Second View" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="zEq-FU-wV5">
<rect key="frame" x="87" y="312" width="201.5" height="43"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loaded by SecondViewController" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NDk-cv-Gan">
<rect key="frame" x="80" y="363" width="215" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
@ -76,10 +108,10 @@
</objects>
<point key="canvasLocation" x="750" y="360"/>
</scene>
<!--Tab Bar Controller-->
<!--Main Tab Bar Controller-->
<scene sceneID="yl2-sM-qoP">
<objects>
<tabBarController id="49e-Tb-3d3" sceneMemberID="viewController">
<tabBarController id="49e-Tb-3d3" customClass="MainTabBarController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<tabBar key="tabBar" contentMode="scaleToFill" id="W28-zg-YXA">
<rect key="frame" x="0.0" y="975" width="768" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
@ -88,6 +120,7 @@
<connections>
<segue destination="9pv-A4-QxB" kind="relationship" relationship="viewControllers" id="u7Y-xg-7CH"/>
<segue destination="8rJ-Kc-sve" kind="relationship" relationship="viewControllers" id="lzU-1b-eKA"/>
<segue destination="Bed-pj-wE9" kind="presentation" identifier="AuthenticateSegue" id="OY3-ks-sfC"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>

13
elpha-ios/Configuration.swift

@ -0,0 +1,13 @@
//
// Config.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/25/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import Foundation
struct Config {
static let clientName = "xyz.elpha.elpha-ios"
}

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

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14135" systemVersion="17G65" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Account" representedClassName="Account" syncable="YES" codeGenerationType="class">
<attribute name="acct" attributeType="String" syncable="YES"/>
<attribute name="avatarData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="avatarStaticUrl" optional="YES" attributeType="URI" syncable="YES"/>
<attribute name="avatarUrl" optional="YES" attributeType="URI" syncable="YES"/>
<attribute name="bot" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="displayName" attributeType="String" syncable="YES"/>
<attribute name="followersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="followingCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<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="locked" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="moved" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="note" attributeType="String" syncable="YES"/>
<attribute name="statusesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="url" attributeType="String" syncable="YES"/>
<attribute name="username" attributeType="String" syncable="YES"/>
</entity>
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="clientId" attributeType="String" syncable="YES"/>
<attribute name="id" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
</entity>
<entity name="Session" representedClassName="Session" syncable="YES" codeGenerationType="class">
<attribute name="color" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="order" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="selected" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" syncable="YES"/>
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="App" syncable="YES"/>
</entity>
<elements>
<element name="Account" positionX="-63" positionY="-18" width="128" height="330"/>
<element name="App" positionX="-54" positionY="135" width="128" height="75"/>
<element name="Session" positionX="-36" positionY="144" width="128" height="135"/>
</elements>
</model>

2
elpha-ios/Info.plist

@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Elpha</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>

124
elpha-ios/KeychainItemAccessibility.swift

@ -0,0 +1,124 @@
//
// KeychainOptions.swift
// SwiftKeychainWrapper
//
// Created by James Blair on 4/24/16.
// Copyright © 2016 Jason Rendel. All rights reserved.
//
// The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
protocol KeychainAttrRepresentable {
var keychainAttrValue: CFString { get }
}
// MARK: - KeychainItemAccessibility
public enum KeychainItemAccessibility {
/**
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
*/
@available(iOS 4, *)
case afterFirstUnlock
/**
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
@available(iOS 4, *)
case afterFirstUnlockThisDeviceOnly
/**
The data in the keychain item can always be accessed regardless of whether the device is locked.
This is not recommended for application use. Items with this attribute migrate to a new device when using encrypted backups.
*/
@available(iOS 4, *)
case always
/**
The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device.
This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted.
*/
@available(iOS 8, *)
case whenPasscodeSetThisDeviceOnly
/**
The data in the keychain item can always be accessed regardless of whether the device is locked.
This is not recommended for application use. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
@available(iOS 4, *)
case alwaysThisDeviceOnly
/**
The data in the keychain item can be accessed only while the device is unlocked by the user.
This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups.
This is the default value for keychain items added without explicitly setting an accessibility constant.
*/
@available(iOS 4, *)
case whenUnlocked
/**
The data in the keychain item can be accessed only while the device is unlocked by the user.
This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
*/
@available(iOS 4, *)
case whenUnlockedThisDeviceOnly
static func accessibilityForAttributeValue(_ keychainAttrValue: CFString) -> KeychainItemAccessibility? {
for (key, value) in keychainItemAccessibilityLookup {
if value == keychainAttrValue {
return key
}
}
return nil
}
}
private let keychainItemAccessibilityLookup: [KeychainItemAccessibility:CFString] = {
var lookup: [KeychainItemAccessibility:CFString] = [
.afterFirstUnlock: kSecAttrAccessibleAfterFirstUnlock,
.afterFirstUnlockThisDeviceOnly: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
.always: kSecAttrAccessibleAlways,
.whenPasscodeSetThisDeviceOnly: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.alwaysThisDeviceOnly : kSecAttrAccessibleAlwaysThisDeviceOnly,
.whenUnlocked: kSecAttrAccessibleWhenUnlocked,
.whenUnlockedThisDeviceOnly: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
return lookup
}()
extension KeychainItemAccessibility : KeychainAttrRepresentable {
internal var keychainAttrValue: CFString {
return keychainItemAccessibilityLookup[self]!
}
}

445
elpha-ios/KeychainWrapper.swift

@ -0,0 +1,445 @@
//
// KeychainWrapper.swift
// KeychainWrapper
//
// Created by Jason Rendel on 9/23/14.
// Copyright (c) 2014 Jason Rendel. All rights reserved.
//
// The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
private let SecMatchLimit: String! = kSecMatchLimit as String
private let SecReturnData: String! = kSecReturnData as String
private let SecReturnPersistentRef: String! = kSecReturnPersistentRef as String
private let SecValueData: String! = kSecValueData as String
private let SecAttrAccessible: String! = kSecAttrAccessible as String
private let SecClass: String! = kSecClass as String
private let SecAttrService: String! = kSecAttrService as String
private let SecAttrGeneric: String! = kSecAttrGeneric as String
private let SecAttrAccount: String! = kSecAttrAccount as String
private let SecAttrAccessGroup: String! = kSecAttrAccessGroup as String
private let SecReturnAttributes: String = kSecReturnAttributes as String
/// KeychainWrapper is a class to help make Keychain access in Swift more straightforward. It is designed to make accessing the Keychain services more like using NSUserDefaults, which is much more familiar to people.
open class KeychainWrapper {
@available(*, deprecated: 2.2.1, message: "KeychainWrapper.defaultKeychainWrapper is deprecated, use KeychainWrapper.standard instead")
public static let defaultKeychainWrapper = KeychainWrapper.standard
/// Default keychain wrapper access
public static let standard = KeychainWrapper()
/// ServiceName is used for the kSecAttrService property to uniquely identify this keychain accessor. If no service name is specified, KeychainWrapper will default to using the bundleIdentifier.
private (set) public var serviceName: String
/// AccessGroup is used for the kSecAttrAccessGroup property to identify which Keychain Access Group this entry belongs to. This allows you to use the KeychainWrapper with shared keychain access between different applications.
private (set) public var accessGroup: String?
private static let defaultServiceName: String = {
return Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper"
}()
private convenience init() {
self.init(serviceName: KeychainWrapper.defaultServiceName)
}
/// Create a custom instance of KeychainWrapper with a custom Service Name and optional custom access group.
///
/// - parameter serviceName: The ServiceName for this instance. Used to uniquely identify all keys stored using this keychain wrapper instance.
/// - parameter accessGroup: Optional unique AccessGroup for this instance. Use a matching AccessGroup between applications to allow shared keychain access.
public init(serviceName: String, accessGroup: String? = nil) {
self.serviceName = serviceName
self.accessGroup = accessGroup
}
// MARK:- Public Methods
/// Checks if keychain data exists for a specified key.
///
/// - parameter forKey: The key to check for.
/// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
/// - returns: True if a value exists for the key. False otherwise.
open func hasValue(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
if let _ = data(forKey: key, withAccessibility: accessibility) {
return true
} else {
return false
}
}
open func accessibilityOfKey(_ key: String) -> KeychainItemAccessibility? {
var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key)
// Remove accessibility attribute
keychainQueryDictionary.removeValue(forKey: SecAttrAccessible)
// Limit search results to one
keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
// Specify we want SecAttrAccessible returned
keychainQueryDictionary[SecReturnAttributes] = kCFBooleanTrue
// Search
var result: AnyObject?
let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
guard status == noErr, let resultsDictionary = result as? [String:AnyObject], let accessibilityAttrValue = resultsDictionary[SecAttrAccessible] as? String else {
return nil
}
return KeychainItemAccessibility.accessibilityForAttributeValue(accessibilityAttrValue as CFString)
}
/// Get the keys of all keychain entries matching the current ServiceName and AccessGroup if one is set.
open func allKeys() -> Set<String> {
var keychainQueryDictionary: [String:Any] = [
SecClass: kSecClassGenericPassword,
SecAttrService: serviceName,
SecReturnAttributes: kCFBooleanTrue,
SecMatchLimit: kSecMatchLimitAll,
]
if let accessGroup = self.accessGroup {
keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
}
var result: AnyObject?
let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
guard status == errSecSuccess else { return [] }
var keys = Set<String>()
if let results = result as? [[AnyHashable: Any]] {
for attributes in results {
if let accountData = attributes[SecAttrAccount] as? Data,
let account = String(data: accountData, encoding: String.Encoding.utf8) {
keys.insert(account)
}
}
}
return keys
}
// MARK: Public Getters
open func integer(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Int? {
guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
return nil
}
return numberValue.intValue
}
open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Float? {
guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
return nil
}
return numberValue.floatValue
}
open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Double? {
guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
return nil
}
return numberValue.doubleValue
}
open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool? {
guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
return nil
}
return numberValue.boolValue
}
/// Returns a string value for a specified key.
///
/// - parameter forKey: The key to lookup data for.
/// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
/// - returns: The String associated with the key if it exists. If no data exists, or the data found cannot be encoded as a string, returns nil.
open func string(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> String? {
guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
return nil
}
return String(data: keychainData, encoding: String.Encoding.utf8) as String?
}
/// Returns an object that conforms to NSCoding for a specified key.
///
/// - parameter forKey: The key to lookup data for.
/// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
/// - returns: The decoded object associated with the key if it exists. If no data exists, or the data found cannot be decoded, returns nil.
open func object(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> NSCoding? {
guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
return nil
}
return NSKeyedUnarchiver.unarchiveObject(with: keychainData) as? NSCoding
}
/// Returns a Data object for a specified key.
///
/// - parameter forKey: The key to lookup data for.
/// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
/// - returns: The Data object associated with the key if it exists. If no data exists, returns nil.
open func data(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
// Limit search results to one
keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
// Specify we want Data/CFData returned
keychainQueryDictionary[SecReturnData] = kCFBooleanTrue
// Search
var result: AnyObject?
let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
return status == noErr ? result as? Data : nil
}
/// Returns a persistent data reference object for a specified key.
///
/// - parameter forKey: The key to lookup data for.
/// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
/// - returns: The persistent data reference object associated with the key if it exists. If no data exists, returns nil.
open func dataRef(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
// Limit search results to one
keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
// Specify we want persistent Data/CFData reference returned
keychainQueryDictionary[SecReturnPersistentRef] = kCFBooleanTrue
// Search
var result: AnyObject?
let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
return status == noErr ? result as? Data : nil
}
// MARK: Public Setters
@discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
}
@discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
}
@discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
}
@discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
}
/// Save a String value to the keychain associated with a specified key. If a String value already exists for the given key, the string will be overwritten with the new value.
///
/// - parameter value: The String value to save.
/// - parameter forKey: The key to save the String under.
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
if let data = value.data(using: .utf8) {
return set(data, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
/// Save an NSCoding compliant object to the keychain associated with a specified key. If an object already exists for the given key, the object will be overwritten with the new value.
///
/// - parameter value: The NSCoding compliant object to save.
/// - parameter forKey: The key to save the object under.
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
let data = NSKeyedArchiver.archivedData(withRootObject: value)
return set(data, forKey: key, withAccessibility: accessibility)
}
/// Save a Data object to the keychain associated with a specified key. If data already exists for the given key, the data will be overwritten with the new value.
///
/// - parameter value: The Data object to save.
/// - parameter forKey: The key to save the object under.
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
keychainQueryDictionary[SecValueData] = value
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
} else {
// Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
}
let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
return update(value, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
@available(*, deprecated: 2.2.1, message: "remove is deprecated, use removeObject instead")
@discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
return removeObject(forKey: key, withAccessibility: accessibility)
}
/// Remove an object associated with a specified key. If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with.
///
/// - parameter forKey: The key value to remove data for.
/// - parameter withAccessibility: Optional accessibility level to use when looking up the keychain item.
/// - returns: True if successful, false otherwise.
@discardableResult open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
// Delete
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}
/// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set.
open func removeAllKeys() -> Bool {
// Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
// Uniquely identify this keychain accessor
keychainQueryDictionary[SecAttrService] = serviceName
// Set the keychain access group if defined
if let accessGroup = self.accessGroup {
keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
}
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}
/// Remove all keychain data, including data not added through keychain wrapper.
///
/// - Warning: This may remove custom keychain entries you did not add via SwiftKeychainWrapper.
///
open class func wipeKeychain() {
deleteKeychainSecClass(kSecClassGenericPassword) // Generic password items
deleteKeychainSecClass(kSecClassInternetPassword) // Internet password items
deleteKeychainSecClass(kSecClassCertificate) // Certificate items
deleteKeychainSecClass(kSecClassKey) // Cryptographic key items
deleteKeychainSecClass(kSecClassIdentity) // Identity items
}
// MARK:- Private Methods
/// Remove all items for a given Keychain Item Class
///
///
@discardableResult private class func deleteKeychainSecClass(_ secClass: AnyObject) -> Bool {
let query = [SecClass: secClass]
let status: OSStatus = SecItemDelete(query as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}
/// Update existing data associated with a specified key name. The existing data will be overwritten by the new data.
private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
let updateDictionary = [SecValueData:value]
// on update, only set accessibility if passed in
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
}
// Update
let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}
/// Setup the keychain query dictionary used to access the keychain on iOS for a specified key name. Takes into account the Service Name and Access Group if one is set.
///
/// - parameter forKey: The key this query is for
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item. If none is provided, will default to .WhenUnlocked
/// - returns: A dictionary with all the needed properties setup to access the keychain on iOS
private func setupKeychainQueryDictionary(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> [String:Any] {
// Setup default access as generic password (rather than a certificate, internet password, etc)
var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
// Uniquely identify this keychain accessor
keychainQueryDictionary[SecAttrService] = serviceName
// Only set accessibiilty if its passed in, we don't want to default it here in case the user didn't want it set
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
}
// Set the keychain access group if defined
if let accessGroup = self.accessGroup {
keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
}
// Uniquely identify the account who will be accessing the keychain
let encodedIdentifier: Data? = key.data(using: String.Encoding.utf8)
keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
keychainQueryDictionary[SecAttrAccount] = encodedIdentifier
return keychainQueryDictionary
}
}

25
elpha-ios/MainTabBarController.swift

@ -0,0 +1,25 @@
//
// MainTabBarController.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/25/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import UIKit
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if AuthenticationManager.shared.sessionCount == 0 {
print("Unauthenticated")
performSegue(withIdentifier: "AuthenticateSegue", sender: self)
return
}
}
}
Loading…
Cancel
Save