Browse Source

Development

master
Dwayne Harris 6 years ago
parent
commit
05efbe9147
  1. 16
      Package.resolved
  2. 28
      Package.swift
  3. 3
      Sources/elpha-ios/elpha_ios.swift
  4. 7
      Tests/LinuxMain.swift
  5. 9
      Tests/elpha-iosTests/XCTestManifests.swift
  6. 16
      Tests/elpha-iosTests/elpha_iosTests.swift
  7. 20
      elpha-ios.xcodeproj/project.pbxproj
  8. 21
      elpha-ios/Assets.xcassets/Instance Placeholder.imageset/Contents.json
  9. BIN
      elpha-ios/Assets.xcassets/Instance Placeholder.imageset/apple-touch-icon.png
  10. 6
      elpha-ios/AuthenticationManager.swift
  11. 131
      elpha-ios/Base.lproj/Main.storyboard
  12. 5
      elpha-ios/Configuration.swift
  13. 23
      elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents
  14. 35
      elpha-ios/ImageCache.swift
  15. 15
      elpha-ios/InstancesNavigationController.swift
  16. 15
      elpha-ios/InstancesTableView.swift
  17. 33
      elpha-ios/InstancesTableViewCell.swift
  18. 202
      elpha-ios/InstancesTableViewController.swift

16
Package.resolved

@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "Kingfisher",
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
"state": {
"branch": null,
"revision": "b6d83f8520a81996738b40d3cbfdad939ceab2b3",
"version": "4.9.0"
}
}
]
},
"version": 1
}

28
Package.swift

@ -0,0 +1,28 @@
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "elpha-ios",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "elpha-ios",
targets: ["elpha-ios"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "elpha-ios",
dependencies: []),
.testTarget(
name: "elpha-iosTests",
dependencies: ["elpha-ios"]),
]
)

3
Sources/elpha-ios/elpha_ios.swift

@ -0,0 +1,3 @@
struct elpha_ios {
var text = "Hello, World!"
}

7
Tests/LinuxMain.swift

@ -0,0 +1,7 @@
import XCTest
import elpha_iosTests
var tests = [XCTestCaseEntry]()
tests += elpha_iosTests.allTests()
XCTMain(tests)

9
Tests/elpha-iosTests/XCTestManifests.swift

@ -0,0 +1,9 @@
import XCTest
#if !os(macOS)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(elpha_iosTests.allTests),
]
}
#endif

16
Tests/elpha-iosTests/elpha_iosTests.swift

@ -0,0 +1,16 @@
import XCTest
@testable import elpha_ios
final class elpha_iosTests: XCTestCase {
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(elpha_ios().text, "Hello, World!")
}
static var allTests = [
("testExample", testExample),
]
}

20
elpha-ios.xcodeproj/project.pbxproj

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
15830D93214F6F6F0037C342 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15830D92214F6F6F0037C342 /* ImageCache.swift */; };
159048AF214F5015004F4014 /* InstancesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159048AE214F5015004F4014 /* InstancesTableViewCell.swift */; };
15960E5B213145E100C38CE9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E5A213145E100C38CE9 /* AppDelegate.swift */; };
15960E5F213145E100C38CE9 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E5E213145E100C38CE9 /* SecondViewController.swift */; };
15960E62213145E100C38CE9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 15960E60213145E100C38CE9 /* Main.storyboard */; };
@ -21,9 +23,14 @@
15960E7E21329FED00C38CE9 /* AuthenticateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */; };
15960E8021353DCF00C38CE9 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E7F21353DCF00C38CE9 /* TimelineViewController.swift */; };
15960E822136668500C38CE9 /* TimelinesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E812136668500C38CE9 /* TimelinesNavigationController.swift */; };
15960E84213774FC00C38CE9 /* InstancesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E83213774FC00C38CE9 /* InstancesTableViewController.swift */; };
15960E862137775D00C38CE9 /* InstancesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E852137775D00C38CE9 /* InstancesNavigationController.swift */; };
15960E88213902A400C38CE9 /* InstancesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15960E87213902A400C38CE9 /* InstancesTableView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
15830D92214F6F6F0037C342 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; 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; };
15960E5A213145E100C38CE9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
15960E5E213145E100C38CE9 /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = "<group>"; };
@ -40,6 +47,9 @@
15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticateViewController.swift; sourceTree = "<group>"; };
15960E7F21353DCF00C38CE9 /* TimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = "<group>"; };
15960E812136668500C38CE9 /* TimelinesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinesNavigationController.swift; sourceTree = "<group>"; };
15960E83213774FC00C38CE9 /* InstancesTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesTableViewController.swift; sourceTree = "<group>"; };
15960E852137775D00C38CE9 /* InstancesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesNavigationController.swift; sourceTree = "<group>"; };
15960E87213902A400C38CE9 /* InstancesTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesTableView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -76,6 +86,7 @@
15960E5A213145E100C38CE9 /* AppDelegate.swift */,
15960E7B213272CD00C38CE9 /* AuthenticationManager.swift */,
15960E7621322C6F00C38CE9 /* Configuration.swift */,
15830D92214F6F6F0037C342 /* ImageCache.swift */,
15960E63213145E200C38CE9 /* Assets.xcassets */,
15960E6E21321FA500C38CE9 /* Elpha.xcdatamodeld */,
15960E7121322B9F00C38CE9 /* Keychain Helper */,
@ -103,6 +114,10 @@
15960E7D21329FED00C38CE9 /* AuthenticateViewController.swift */,
15960E7F21353DCF00C38CE9 /* TimelineViewController.swift */,
15960E812136668500C38CE9 /* TimelinesNavigationController.swift */,
15960E83213774FC00C38CE9 /* InstancesTableViewController.swift */,
15960E852137775D00C38CE9 /* InstancesNavigationController.swift */,
15960E87213902A400C38CE9 /* InstancesTableView.swift */,
159048AE214F5015004F4014 /* InstancesTableViewCell.swift */,
);
name = "View Controllers";
sourceTree = "<group>";
@ -178,6 +193,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
15960E88213902A400C38CE9 /* InstancesTableView.swift in Sources */,
159048AF214F5015004F4014 /* InstancesTableViewCell.swift in Sources */,
15830D93214F6F6F0037C342 /* ImageCache.swift in Sources */,
15960E84213774FC00C38CE9 /* InstancesTableViewController.swift in Sources */,
15960E7021321FA500C38CE9 /* Elpha.xcdatamodeld in Sources */,
15960E5F213145E100C38CE9 /* SecondViewController.swift in Sources */,
15960E8021353DCF00C38CE9 /* TimelineViewController.swift in Sources */,
@ -188,6 +207,7 @@
15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */,
15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */,
15960E7321322BC700C38CE9 /* KeychainItemAccessibility.swift in Sources */,
15960E862137775D00C38CE9 /* InstancesNavigationController.swift in Sources */,
15960E822136668500C38CE9 /* TimelinesNavigationController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

21
elpha-ios/Assets.xcassets/Instance Placeholder.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "apple-touch-icon.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
elpha-ios/Assets.xcassets/Instance Placeholder.imageset/apple-touch-icon.png

After

Width: 180  |  Height: 180  |  Size: 4.8 KiB

6
elpha-ios/AuthenticationManager.swift

@ -16,7 +16,7 @@ class AuthenticationManager {
var sessionCount: Int {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<Session>(entityName: "Session")
let request = NSFetchRequest<SessionMO>(entityName: "Session")
do {
return try context.count(for: request)
@ -28,10 +28,10 @@ class AuthenticationManager {
}
}
var selectedSession: Session? {
var selectedSession: SessionMO? {
get {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<Session>(entityName: "Session")
let request = NSFetchRequest<SessionMO>(entityName: "Session")
request.predicate = NSPredicate(format: "selected = YES")
do {

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

@ -6,6 +6,7 @@
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -70,6 +71,9 @@
<state key="normal" title="Find an Instance">
<color key="titleColor" red="0.090196078430000007" green="0.047058823530000002" blue="0.28627450980000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<segue destination="SqQ-GI-iCq" kind="show" id="p16-wD-rWY"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.72941176470588232" green="0.6705882352941176" blue="0.83921568627450982" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -119,6 +123,107 @@
</objects>
<point key="canvasLocation" x="-615.20000000000005" y="770.46476761619192"/>
</scene>
<!--Instances Table View Controller-->
<scene sceneID="24Q-ad-dey">
<objects>
<tableViewController id="bRx-56-pJs" customClass="InstancesTableViewController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="120" sectionHeaderHeight="28" sectionFooterHeight="28" id="MsU-Kh-W9V" customClass="InstancesTableView" customModule="elpha_ios" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="InstancesTableViewCell" rowHeight="120" id="t5I-4e-rPV" customClass="InstancesTableViewCell" customModule="elpha_ios" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="120"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="t5I-4e-rPV" id="Awg-lg-91F">
<rect key="frame" x="0.0" y="0.0" width="375" height="119.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Instance Placeholder" translatesAutoresizingMaskIntoConstraints="NO" id="3lE-eD-RSI">
<rect key="frame" x="16" y="11" width="100" height="98"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="Sf5-1G-8gX"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="scl-0p-jQO">
<rect key="frame" x="124" y="26" width="227" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="v20-Wv-cjD"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Statuses" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GvU-C8-JYy">
<rect key="frame" x="124" y="64" width="60" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="FNh-pN-i9Y"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.66422420739999999" green="0.66424006219999998" blue="0.66423153879999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4n9-eS-zCc">
<rect key="frame" x="124" y="84" width="60" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="ddf-nO-YtD"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Users" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x17-3V-ix0">
<rect key="frame" x="192" y="64" width="35.5" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.66422420739999999" green="0.66424006219999998" blue="0.66423153879999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4fE-NE-pXx">
<rect key="frame" x="192" y="84" width="38" height="17"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="scl-0p-jQO" secondAttribute="trailing" constant="8" id="9a9-Za-5xy"/>
<constraint firstAttribute="bottomMargin" secondItem="3lE-eD-RSI" secondAttribute="bottom" id="C3d-Xw-1NM"/>
<constraint firstItem="3lE-eD-RSI" firstAttribute="top" secondItem="Awg-lg-91F" secondAttribute="topMargin" id="E5j-M3-x38"/>
<constraint firstItem="GvU-C8-JYy" firstAttribute="leading" secondItem="3lE-eD-RSI" secondAttribute="trailing" constant="8" id="L1f-Ou-oJM"/>
<constraint firstItem="GvU-C8-JYy" firstAttribute="top" secondItem="scl-0p-jQO" secondAttribute="bottom" constant="8" id="NWZ-Se-Yo2"/>
<constraint firstItem="x17-3V-ix0" firstAttribute="leading" secondItem="GvU-C8-JYy" secondAttribute="trailing" constant="8" id="SKd-eL-xTg"/>
<constraint firstItem="x17-3V-ix0" firstAttribute="top" secondItem="scl-0p-jQO" secondAttribute="bottom" constant="8" id="V9U-Fm-AlT"/>
<constraint firstItem="4fE-NE-pXx" firstAttribute="top" secondItem="x17-3V-ix0" secondAttribute="bottom" constant="4" id="Xuc-9Y-4VO"/>
<constraint firstItem="scl-0p-jQO" firstAttribute="leading" secondItem="3lE-eD-RSI" secondAttribute="trailing" constant="8" id="Ybe-wP-f47"/>
<constraint firstItem="4fE-NE-pXx" firstAttribute="leading" secondItem="4n9-eS-zCc" secondAttribute="trailing" constant="8" id="aGK-30-q1o"/>
<constraint firstItem="4n9-eS-zCc" firstAttribute="top" secondItem="GvU-C8-JYy" secondAttribute="bottom" constant="4" id="kRr-tr-bPQ"/>
<constraint firstItem="scl-0p-jQO" firstAttribute="top" secondItem="Awg-lg-91F" secondAttribute="topMargin" constant="15" id="pf7-QJ-dXi"/>
<constraint firstItem="4n9-eS-zCc" firstAttribute="leading" secondItem="3lE-eD-RSI" secondAttribute="trailing" constant="8" id="rLU-Oz-WCd"/>
<constraint firstItem="3lE-eD-RSI" firstAttribute="leading" secondItem="Awg-lg-91F" secondAttribute="leadingMargin" id="uVI-Cr-1hQ"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="instanceNameLabel" destination="scl-0p-jQO" id="Yzj-Za-0fb"/>
<outlet property="statusesLabel" destination="4n9-eS-zCc" id="NQe-PG-1OF"/>
<outlet property="thumbnailImageView" destination="3lE-eD-RSI" id="4XF-Ca-fIV"/>
<outlet property="usersLabel" destination="4fE-NE-pXx" id="Svc-5P-YTj"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="bRx-56-pJs" id="gzn-gi-sZX"/>
<outlet property="delegate" destination="bRx-56-pJs" id="PeE-yA-Kud"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="mfC-MW-ufJ"/>
<connections>
<outlet property="mainNavigationItem" destination="mfC-MW-ufJ" id="YZK-rX-Gn3"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pnx-Ji-ouu" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1064.8" y="1048.4257871064469"/>
</scene>
<!--Second-->
<scene sceneID="wg7-f3-ORb">
<objects>
@ -196,8 +301,34 @@
</objects>
<point key="canvasLocation" x="749.60000000000002" y="-320.68965517241384"/>
</scene>
<!--Instances Navigation Controller-->
<scene sceneID="UVY-Um-jQm">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="SqQ-GI-iCq" customClass="InstancesNavigationController" customModule="elpha_ios" customModuleProvider="target" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="QFN-sS-62X">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</textAttributes>
<textAttributes key="largeTitleTextAttributes">
<color key="textColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</textAttributes>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="bRx-56-pJs" kind="relationship" relationship="rootViewController" id="G19-q1-oJk"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pTB-k2-GBg" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="125.59999999999999" y="1048.4257871064469"/>
</scene>
</scenes>
<resources>
<image name="Instance Placeholder" width="60" height="60"/>
<image name="Logo" width="400" height="400"/>
<image name="Timelines" width="25" height="25"/>
<image name="second" width="30" height="30"/>

5
elpha-ios/Configuration.swift

@ -10,4 +10,9 @@ import Foundation
struct Config {
static let clientName = "xyz.elpha.elpha-ios"
static let instancesServiceUrl = "https://instances.social"
static let instancesServiceEndpoint = "/api/1.0/instances/list"
static let instancesServiceApplicationID = "104969750"
static let instancesServiceSecret = "xEGMUQL9w2tYU7CnkBMY555nmLYL1dryCdQNFZWQlwHqDg0cRPxF5nXx34LVM5Zt8CbvmNnj89nruMYe0jCzlky0lolAcoLOcNrNerh9m30Q85WskiOqEkHp0M5HhEeS"
}

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

@ -1,6 +1,6 @@
<?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">
<entity name="Account" representedClassName="AccountMO" 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"/>
@ -21,11 +21,27 @@
<attribute name="url" attributeType="String" syncable="YES"/>
<attribute name="username" attributeType="String" syncable="YES"/>
</entity>
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<entity name="App" representedClassName="AppMO" 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">
<entity name="Instance" representedClassName="InstanceMO" syncable="YES" codeGenerationType="class">
<attribute name="connections" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="desc" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="email" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="https" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="languages" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="registrations" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="statuses" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="thumbnail" optional="YES" attributeType="URI" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uptime" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uri" attributeType="URI" syncable="YES"/>
<attribute name="urls" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="users" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="version" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Session" representedClassName="SessionMO" 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"/>
@ -36,6 +52,7 @@
<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="Instance" positionX="-18" positionY="153" width="128" height="255"/>
<element name="Session" positionX="-36" positionY="144" width="128" height="135"/>
</elements>
</model>

35
elpha-ios/ImageCache.swift

@ -0,0 +1,35 @@
//
// ImageCache.swift
// elpha-ios
//
// Created by Dwayne Harris on 9/16/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import Foundation
import UIKit
class ImageCache {
var cache: NSCache = NSCache<NSString, UIImage>()
static let shared = ImageCache()
func getImage(forURL imageURL: URL, completion: @escaping (UIImage?, Error?) -> Void) {
if let cachedImage = cache.object(forKey: imageURL.absoluteString as NSString) {
completion(cachedImage, nil)
} else {
URLSession.shared.dataTask(with: URLRequest(url: imageURL)) { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
if let data = data, let image = UIImage(data: data) {
self.cache.setObject(image, forKey: imageURL.absoluteString as NSString)
completion(image, nil)
} else {
completion(nil, NSError())
}
}.resume()
}
}
}

15
elpha-ios/InstancesNavigationController.swift

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

15
elpha-ios/InstancesTableView.swift

@ -0,0 +1,15 @@
//
// InstancesTableView.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/30/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import UIKit
class InstancesTableView: UITableView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

33
elpha-ios/InstancesTableViewCell.swift

@ -0,0 +1,33 @@
//
// InstancesTableViewCell.swift
// elpha-ios
//
// Created by Dwayne Harris on 9/16/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import Foundation
import UIKit
class InstancesTableViewCell: UITableViewCell {
@IBOutlet var thumbnailImageView: UIImageView!
@IBOutlet var instanceNameLabel: UILabel!
@IBOutlet var statusesLabel: UILabel!
@IBOutlet var usersLabel: UILabel!
var thumbnailURL: String? {
didSet {
if let thumbnailURL = thumbnailURL {
ImageCache.shared.getImage(forURL: URL(string: thumbnailURL)!) { image, error in
guard error == nil else {
return
}
DispatchQueue.main.async {
self.thumbnailImageView.image = image
}
}
}
}
}
}

202
elpha-ios/InstancesTableViewController.swift

@ -0,0 +1,202 @@
//
// InstancesTableViewController.swift
// elpha-ios
//
// Created by Dwayne Harris on 8/29/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import CoreData
import UIKit
struct Instance {
var id: String?
var name: String?
var addedAt: String?
var updatedAt: String?
var checkedAt: String?
var uptime: Int?
var up: Bool?
var dead: Bool?
var version: String?
var ipv6: Bool?
var httpsScore: Int?
var httpsRank: String?
var users: String?
var statuses: String?
var connections: Int?
var openRegistrations: Bool?
var thumbnail: String?
var thumbnailProxy: String?
var activeUsers: Int?
var shortDescription: String?
var fullDescription: String?
var topic: String?
var languages: [String]?
var categories: [String]?
}
class InstancesTableViewController: UITableViewController {
@IBOutlet var mainNavigationItem: UINavigationItem!
let instanceRequestCount = 20
var instances: [Instance] = []
var nextID: String? = nil
var loading: Bool = false
var finished: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
UINavigationBar.appearance().barTintColor = UIColor.red
UINavigationBar.appearance().tintColor = UIColor.white
mainNavigationItem.title = "Mastodon Instances"
mainNavigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
loadInstances()
}
@objc func done() {
dismiss(animated: true, completion: nil)
}
func loadInstances() {
loading = true
var params = [
"count=\(instanceRequestCount)",
"sort_by=statuses",
"sort_order=desc",
"include_closed=true",
"include_down=false",
]
if let nextID = nextID {
params.append("min_id=\(nextID)")
} else {
instances = []
}
let requestURL = "\(Config.instancesServiceUrl)\(Config.instancesServiceEndpoint)?\(params.joined(separator: "&"))"
print("loading instances: requestURL: \(requestURL)")
var request = URLRequest(url: URL(string: requestURL)!)
request.addValue("Bearer \(Config.instancesServiceSecret)", forHTTPHeaderField: "Authorization")
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
return
}
defer {
self.loading = false
}
if let response = response as? HTTPURLResponse {
switch response.statusCode {
case 200..<300:
if let data = data {
do {
let instancesResult = try JSONSerialization.jsonObject(with: data) as! [String: Any]
if let pagination = instancesResult["pagination"] as? [String: Any] {
if let nextID = pagination["next_id"] as? String {
self.nextID = nextID
} else {
self.nextID = nil
self.finished = true
}
}
if let instances = instancesResult["instances"] as? [Any] {
for case let instance as [String: Any] in instances {
var i = Instance()
i.id = instance["id"] as? String
i.name = instance["name"] as? String
i.addedAt = instance["added_at"] as? String
i.updatedAt = instance["updated_at"] as? String
i.checkedAt = instance["checked_at"] as? String
i.uptime = instance["uptime"] as? Int
i.up = instance["up"] as? Bool
i.dead = instance["dead"] as? Bool
i.version = instance["version"] as? String
i.ipv6 = instance["ipv6"] as? Bool
i.httpsScore = instance["https_score"] as? Int
i.httpsRank = instance["https_rank"] as? String
i.users = instance["users"] as? String
i.statuses = instance["statuses"] as? String
i.connections = instance["connections"] as? Int
i.openRegistrations = instance["open_registrations"] as? Bool
i.thumbnail = instance["thumbnail"] as? String
i.thumbnailProxy = instance["thumbnail_proxy"] as? String
i.activeUsers = instance["active_users"] as? Int
let info = instance["info"] as? [String: Any]
if let info = info {
i.shortDescription = info["short_description"] as? String
i.fullDescription = info["full_description"] as? String
i.topic = info["topic"] as? String
i.languages = info["languages"] as? [String]
i.categories = info["categories"] as? [String]
}
self.instances.append(i)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch let error {
print(error)
return
}
}
case 400:
print("instances.social: 400")
default:
return
}
}
}
dataTask.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return instances.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "InstancesTableViewCell", for: indexPath) as? InstancesTableViewCell else {
fatalError("Unable to find reusable cell")
}
let instance = instances[indexPath.row]
cell.instanceNameLabel.text = instance.name
if let statuses = instance.statuses {
cell.statusesLabel.text = NumberFormatter.localizedString(from: NSNumber(value: Int64(statuses)!), number: .decimal)
}
if let users = instance.users {
cell.usersLabel.text = NumberFormatter.localizedString(from: NSNumber(value: Int64(users)!), number: .decimal)
}
if let thumbnail = instance.thumbnail {
cell.thumbnailURL = thumbnail
}
if indexPath.row == instances.count - 1 && !loading && !finished {
loadInstances()
}
return cell
}
}
Loading…
Cancel
Save