SwiftUI login screen with router view, updated tag icon

This commit is contained in:
aj 2020-04-25 15:44:54 +01:00
parent 49e558fa85
commit 6d3929a173
11 changed files with 73 additions and 320 deletions

View File

@ -11,16 +11,15 @@
E906F7F724143AA7004E1E31 /* SwiftUICharts in Frameworks */ = {isa = PBXBuildFile; productRef = E906F7F624143AA7004E1E31 /* SwiftUICharts */; };
E92F94822401412100B6B721 /* SwiftUIRefresh in Frameworks */ = {isa = PBXBuildFile; productRef = E92F94812401412100B6B721 /* SwiftUIRefresh */; };
E934AC99240DD0E4009869F4 /* AddTagSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E934AC98240DD0E4009869F4 /* AddTagSheet.swift */; };
E971F8B9245462D500B543B6 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = E971F8B8245462D500B543B6 /* Router.swift */; };
E97AF45623FC4E7800635494 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45523FC4E7800635494 /* User.swift */; };
E97AF45923FC50EC00635494 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = E97AF45823FC50EC00635494 /* SwiftyJSON */; };
E97AF45B23FC748D00635494 /* UserApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45A23FC748D00635494 /* UserApi.swift */; };
E97AF45E23FC83AF00635494 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = E97AF45D23FC83AF00635494 /* KeychainAccess */; };
E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF45F23FC85D600635494 /* PlaylistApi.swift */; };
E97AF46223FC89CC00635494 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E97AF46123FC89CB00635494 /* Main.storyboard */; };
E97AF46423FD4EEF00635494 /* LiveUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46323FD4EEF00635494 /* LiveUser.swift */; };
E97AF46723FD650800635494 /* AddPlaylistSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46623FD650800635494 /* AddPlaylistSheet.swift */; };
E97AF46923FD9E1B00635494 /* PlaylistInputList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */; };
E97AF46C23FDA90900635494 /* LoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97AF46B23FDA90900635494 /* LoginController.swift */; };
E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254BC23F9B7A90056D9D3 /* Playlist.swift */; };
E98254C223F9FFF90056D9D3 /* PlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254C123F9FFF90056D9D3 /* PlaylistView.swift */; };
E98254CA23FA26600056D9D3 /* PlaylistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98254C923FA26600056D9D3 /* PlaylistRow.swift */; };
@ -35,7 +34,7 @@
E9E30C3323FF255C00574EEF /* SettingsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E30C3223FF255C00574EEF /* SettingsList.swift */; };
E9EA690B23F9A5430012C3E8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EA690A23F9A5430012C3E8 /* AppDelegate.swift */; };
E9EA690D23F9A5430012C3E8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EA690C23F9A5430012C3E8 /* SceneDelegate.swift */; };
E9EA690F23F9A5430012C3E8 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EA690E23F9A5430012C3E8 /* RootView.swift */; };
E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EA690E23F9A5430012C3E8 /* AppSkeleton.swift */; };
E9EA691123F9A54A0012C3E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E9EA691023F9A54A0012C3E8 /* Assets.xcassets */; };
E9EA691423F9A54B0012C3E8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E9EA691323F9A54B0012C3E8 /* Preview Assets.xcassets */; };
E9EA691723F9A54B0012C3E8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9EA691523F9A54B0012C3E8 /* LaunchScreen.storyboard */; };
@ -63,14 +62,13 @@
/* Begin PBXFileReference section */
E906F7F32414019C004E1E31 /* NetworkPersister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPersister.swift; sourceTree = "<group>"; };
E934AC98240DD0E4009869F4 /* AddTagSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTagSheet.swift; sourceTree = "<group>"; };
E971F8B8245462D500B543B6 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
E97AF45523FC4E7800635494 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
E97AF45A23FC748D00635494 /* UserApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserApi.swift; sourceTree = "<group>"; };
E97AF45F23FC85D600635494 /* PlaylistApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistApi.swift; sourceTree = "<group>"; };
E97AF46123FC89CB00635494 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
E97AF46323FD4EEF00635494 /* LiveUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveUser.swift; sourceTree = "<group>"; };
E97AF46623FD650800635494 /* AddPlaylistSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPlaylistSheet.swift; sourceTree = "<group>"; };
E97AF46823FD9E1B00635494 /* PlaylistInputList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistInputList.swift; sourceTree = "<group>"; };
E97AF46B23FDA90900635494 /* LoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginController.swift; sourceTree = "<group>"; };
E98254BC23F9B7A90056D9D3 /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; sourceTree = "<group>"; };
E98254C123F9FFF90056D9D3 /* PlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistView.swift; sourceTree = "<group>"; };
E98254C923FA26600056D9D3 /* PlaylistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistRow.swift; sourceTree = "<group>"; };
@ -85,7 +83,7 @@
E9EA690723F9A5430012C3E8 /* Music Tools.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Music Tools.app"; sourceTree = BUILT_PRODUCTS_DIR; };
E9EA690A23F9A5430012C3E8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E9EA690C23F9A5430012C3E8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
E9EA690E23F9A5430012C3E8 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
E9EA690E23F9A5430012C3E8 /* AppSkeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSkeleton.swift; sourceTree = "<group>"; };
E9EA691023F9A54A0012C3E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E9EA691323F9A54B0012C3E8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
E9EA691623F9A54B0012C3E8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@ -128,14 +126,6 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
E97AF46A23FDA8ED00635494 /* Controller */ = {
isa = PBXGroup;
children = (
E97AF46B23FDA90900635494 /* LoginController.swift */,
);
path = Controller;
sourceTree = "<group>";
};
E98254BE23F9BD540056D9D3 /* Model */ = {
isa = PBXGroup;
children = (
@ -165,7 +155,7 @@
E9E30C3423FF256100574EEF /* Settings */,
E9E30C2F23FEACF700574EEF /* Tag */,
E9E30C2E23FEACDE00574EEF /* Playlist */,
E9EA690E23F9A5430012C3E8 /* RootView.swift */,
E9EA690E23F9A5430012C3E8 /* AppSkeleton.swift */,
E98254CF23FB00B60056D9D3 /* LoginScreen.swift */,
);
path = Views;
@ -177,7 +167,6 @@
E9EA691523F9A54B0012C3E8 /* LaunchScreen.storyboard */,
E9EA690A23F9A5430012C3E8 /* AppDelegate.swift */,
E9EA690C23F9A5430012C3E8 /* SceneDelegate.swift */,
E97AF46123FC89CB00635494 /* Main.storyboard */,
);
path = Application;
sourceTree = "<group>";
@ -237,12 +226,12 @@
children = (
E98254C623FA25280056D9D3 /* Application */,
E9EA691023F9A54A0012C3E8 /* Assets.xcassets */,
E97AF46A23FDA8ED00635494 /* Controller */,
E9EA691823F9A54B0012C3E8 /* Info.plist */,
E98254BE23F9BD540056D9D3 /* Model */,
E98254BF23F9BE040056D9D3 /* Network */,
E9EA691223F9A54B0012C3E8 /* Preview Content */,
E98254C023F9FFDD0056D9D3 /* Views */,
E971F8B8245462D500B543B6 /* Router.swift */,
);
path = "Music Tools";
sourceTree = "<group>";
@ -394,7 +383,6 @@
E9EA691723F9A54B0012C3E8 /* LaunchScreen.storyboard in Resources */,
E9EA691423F9A54B0012C3E8 /* Preview Assets.xcassets in Resources */,
E9EA691123F9A54A0012C3E8 /* Assets.xcassets in Resources */,
E97AF46223FC89CC00635494 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -430,11 +418,11 @@
E9EA690D23F9A5430012C3E8 /* SceneDelegate.swift in Sources */,
E98254DB23FB64740056D9D3 /* Network.swift in Sources */,
E9E30C2A23FEAA3A00574EEF /* TagRow.swift in Sources */,
E97AF46C23FDA90900635494 /* LoginController.swift in Sources */,
E97AF46023FC85D600635494 /* PlaylistApi.swift in Sources */,
E9EA690F23F9A5430012C3E8 /* RootView.swift in Sources */,
E9EA690F23F9A5430012C3E8 /* AppSkeleton.swift in Sources */,
E98254BD23F9B7A90056D9D3 /* Playlist.swift in Sources */,
E97AF46723FD650800635494 /* AddPlaylistSheet.swift in Sources */,
E971F8B9245462D500B543B6 /* Router.swift in Sources */,
E98254C223F9FFF90056D9D3 /* PlaylistView.swift in Sources */,
E97AF46423FD4EEF00635494 /* LiveUser.swift in Sources */,
E97AF45623FC4E7800635494 /* User.swift in Sources */,

View File

@ -1,179 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Spt-Mn-jmg">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="a5Z-le-CZi">
<objects>
<viewController id="Spt-Mn-jmg" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="kc5-Ke-ZcW">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="34" translatesAutoresizingMaskIntoConstraints="NO" id="9nJ-jm-QSN">
<rect key="frame" x="-49" y="105" width="512" height="686"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="MusicToolsLogo" translatesAutoresizingMaskIntoConstraints="NO" id="ZdO-HY-xSA">
<rect key="frame" x="0.0" y="0.0" width="512" height="512"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1SI-H8-Rgg">
<rect key="frame" x="215.5" y="546" width="81" height="53"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<state key="normal" title="Login"/>
<connections>
<segue destination="aYE-zJ-V1n" kind="show" id="Lhx-kz-pef"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6UM-RX-7fe">
<rect key="frame" x="196" y="633" width="120" height="53"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<state key="normal" title="Register"/>
<connections>
<segue destination="TND-IP-OdM" kind="show" id="96d-mV-baq"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="6UM-RX-7fe" firstAttribute="top" secondItem="1SI-H8-Rgg" secondAttribute="bottom" constant="34" id="Mc6-3Y-SWR"/>
</constraints>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="9nJ-jm-QSN" firstAttribute="centerX" secondItem="kc5-Ke-ZcW" secondAttribute="centerX" id="cpW-xl-QNm"/>
<constraint firstItem="9nJ-jm-QSN" firstAttribute="centerY" secondItem="kc5-Ke-ZcW" secondAttribute="centerY" id="kjc-76-4D7"/>
</constraints>
<viewLayoutGuide key="safeArea" id="AaY-2f-fg2"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="0B7-E4-M4W" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1443.4782608695652" y="-185.49107142857142"/>
</scene>
<!--View Controller-->
<scene sceneID="6At-fb-bfN">
<objects>
<viewController id="TND-IP-OdM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="MhD-yZ-pD8">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="VLy-9d-bfF"/>
</view>
<navigationItem key="navigationItem" id="ols-n0-cLe"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Dgg-BJ-VHU" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="48" y="304"/>
</scene>
<!--Hosting Controller-->
<scene sceneID="jWQ-vc-Ihe">
<objects>
<hostingController id="fqF-FS-fj3" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="WZ3-YV-Zxu"/>
</hostingController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7Vf-Lo-xr9" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1728" y="-132"/>
</scene>
<!--Login Controller-->
<scene sceneID="Cbq-wX-wg3">
<objects>
<viewController id="aYE-zJ-V1n" customClass="LoginController" customModule="Music_Tools" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="cjE-Uz-adD">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="MusicToolsLogo" translatesAutoresizingMaskIntoConstraints="NO" id="Aa0-TI-VfP">
<rect key="frame" x="0.0" y="50" width="414" height="423"/>
</imageView>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="bezel" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Jsz-1T-t1A">
<rect key="frame" x="20" y="535" width="374" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="ThQ-Jz-RB7"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" textContentType="username"/>
</textField>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="bezel" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="c5V-bv-fGr">
<rect key="frame" x="20" y="628" width="374" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="ynd-9a-coG"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="go" secureTextEntry="YES" textContentType="password"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6YH-OC-12B">
<rect key="frame" x="185.5" y="712" width="43" height="53"/>
<constraints>
<constraint firstAttribute="height" constant="53" id="udu-LZ-6uK"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<state key="normal" title="Go">
<color key="titleShadowColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="doLogin:" destination="aYE-zJ-V1n" eventType="touchUpInside" id="J5V-GY-yG6"/>
<segue destination="fqF-FS-fj3" kind="showDetail" identifier="loginToMain" destinationCreationSelector="returnUIView:" id="xcT-J7-nTv"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CNh-mk-uV0">
<rect key="frame" x="167.5" y="506" width="79" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="oKd-GV-oVN"/>
</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="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mvf-wa-Hkg">
<rect key="frame" x="170" y="599" width="74" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="glG-PD-dHg"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="Jsz-1T-t1A" firstAttribute="leading" secondItem="c5V-bv-fGr" secondAttribute="leading" id="0wx-Ol-kyH"/>
<constraint firstItem="Aa0-TI-VfP" firstAttribute="top" secondItem="JQv-rE-o2m" secondAttribute="top" constant="50" id="36L-3g-C8l"/>
<constraint firstItem="JQv-rE-o2m" firstAttribute="bottom" secondItem="6YH-OC-12B" secondAttribute="bottom" constant="43" id="4I4-MB-XEO"/>
<constraint firstItem="c5V-bv-fGr" firstAttribute="top" secondItem="mvf-wa-Hkg" secondAttribute="bottom" constant="8" id="57A-nQ-c7X"/>
<constraint firstItem="Aa0-TI-VfP" firstAttribute="centerX" secondItem="CNh-mk-uV0" secondAttribute="centerX" id="CSz-Z9-0h2"/>
<constraint firstItem="Jsz-1T-t1A" firstAttribute="trailing" secondItem="cjE-Uz-adD" secondAttribute="trailingMargin" id="JUi-Dm-bqV"/>
<constraint firstItem="CNh-mk-uV0" firstAttribute="centerX" secondItem="Jsz-1T-t1A" secondAttribute="centerX" id="OGV-Rj-ryd"/>
<constraint firstItem="mvf-wa-Hkg" firstAttribute="top" secondItem="Jsz-1T-t1A" secondAttribute="bottom" constant="30" id="V2C-dy-adh"/>
<constraint firstItem="Jsz-1T-t1A" firstAttribute="top" secondItem="CNh-mk-uV0" secondAttribute="bottom" constant="8" id="WVP-qB-wVP"/>
<constraint firstItem="6YH-OC-12B" firstAttribute="top" secondItem="c5V-bv-fGr" secondAttribute="bottom" constant="50" id="ah4-pZ-kNm"/>
<constraint firstItem="c5V-bv-fGr" firstAttribute="centerX" secondItem="6YH-OC-12B" secondAttribute="centerX" id="b6B-2x-Zf2"/>
<constraint firstItem="Jsz-1T-t1A" firstAttribute="trailing" secondItem="c5V-bv-fGr" secondAttribute="trailing" id="bsE-gu-f8q"/>
<constraint firstItem="Aa0-TI-VfP" firstAttribute="leading" secondItem="JQv-rE-o2m" secondAttribute="leading" id="mrX-sL-EaE"/>
<constraint firstItem="Jsz-1T-t1A" firstAttribute="leading" secondItem="cjE-Uz-adD" secondAttribute="leadingMargin" id="q1D-Aj-DhS"/>
<constraint firstItem="mvf-wa-Hkg" firstAttribute="centerX" secondItem="c5V-bv-fGr" secondAttribute="centerX" id="rPo-wh-BDa"/>
<constraint firstItem="CNh-mk-uV0" firstAttribute="top" secondItem="Aa0-TI-VfP" secondAttribute="bottom" constant="33" id="yXw-NR-ADJ"/>
</constraints>
<viewLayoutGuide key="safeArea" id="JQv-rE-o2m"/>
</view>
<navigationItem key="navigationItem" id="otB-Y9-KWi"/>
<connections>
<outlet property="passwordField" destination="c5V-bv-fGr" id="9LJ-hb-7Jx"/>
<outlet property="usernameField" destination="Jsz-1T-t1A" id="QOC-fI-XsH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="BJs-Wv-17o" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-68.115942028985515" y="-676.33928571428567"/>
</scene>
</scenes>
<resources>
<image name="MusicToolsLogo" width="512" height="512"/>
</resources>
</document>

View File

@ -20,7 +20,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let contentView = RootView()
let contentView = Router()
let keychain = Keychain(service: "xyz.sarsoo.music.login")
@ -28,17 +28,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
var controller: UIViewController
if keychain["username"] != nil && keychain["password"] != nil {
let liveUser = LiveUser(playlists: [], tags: [], username: keychain["username"]!).loadUserDefaults()
controller = UIHostingController(rootView: contentView.environmentObject(liveUser))
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
controller = storyboard.instantiateInitialViewController()!
}
let liveUser = LiveUser(playlists: [], tags: [], username: keychain["username"] ?? "", loggedIn: false).loadUserDefaults()
window.rootViewController = controller
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser))
// window.rootViewController = UIHostingController(rootView: contentView.environmentObject(liveUser))
// window.rootViewController = LoginController()
self.window = window

View File

@ -1,96 +0,0 @@
//
// LoginController.swift
// Music Tools
//
// Created by Ellie McCarthy on 19/02/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import UIKit
import SwiftUI
import KeychainAccess
class LoginController: UIViewController, UITextFieldDelegate {
// MARK: Properties
@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var passwordField: UITextField!
@IBOutlet weak var goButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// MARK: Actions
@IBSegueAction func returnUIView(_ coder: NSCoder) -> UIViewController? {
// TODO add right username
let liveUser = LiveUser(playlists: [], tags: [], username: "")
return UIHostingController(coder: coder, rootView: RootView().environmentObject(liveUser))
}
var isLoggedIn: Bool? = nil {
didSet {
if self.isLoggedIn == true {
self.performSegue(withIdentifier: "loginToMain", sender: self)
} else if self.isLoggedIn == false {
debugPrint("false logged in")
self.isLoggedIn = nil
} else {
debugPrint("nil state")
}
}
}
@IBAction func doLogin(_ sender: Any) {
let keychain = Keychain(service: "xyz.sarsoo.music.login")
keychain["username"] = usernameField.text
keychain["password"] = passwordField.text
let api = UserApi.getUser
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
switch response.result {
case .success:
self.isLoggedIn = true
break
case .failure(let error):
debugPrint("error: \(error)")
self.isLoggedIn = false
do {
try keychain.remove("username")
try keychain.remove("password")
} catch let error {
debugPrint("Could not clear keychain, \(error)")
}
break
}
}
}
// block initial segue from button presson
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { return false }
// MARK: UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}

View File

@ -39,8 +39,6 @@
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
@ -60,6 +58,7 @@
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>

View File

@ -16,13 +16,20 @@ class LiveUser: ObservableObject {
@Published var tags: [Tag]
@Published var username: String
@Published var loggedIn: Bool {
didSet {
UserDefaults.standard.set(loggedIn, forKey: "loggedIn")
}
}
@Published var isRefreshingPlaylists = false
@Published var isRefreshingTags = false
init(playlists: [Playlist], tags: [Tag], username: String) {
init(playlists: [Playlist], tags: [Tag], username: String, loggedIn: Bool) {
self.playlists = playlists
self.tags = tags
self.username = username
self.loggedIn = loggedIn
}
func updatePlaylist(playlistIn: Playlist) {
@ -98,6 +105,7 @@ class LiveUser: ObservableObject {
let _strPlaylists = defaults.string(forKey: "playlists")
let _strTags = defaults.string(forKey: "tags")
loggedIn = defaults.bool(forKey: "loggedIn")
do {
if let _strPlaylists = _strPlaylists {

30
Music Tools/Router.swift Normal file
View File

@ -0,0 +1,30 @@
//
// Router.swift
// Music Tools
//
// Created by Andy Pack on 25/04/2020.
// Copyright © 2020 Sarsoo. All rights reserved.
//
import SwiftUI
struct Router: View {
@EnvironmentObject var liveUser: LiveUser
@ViewBuilder
var body: some View {
if liveUser.loggedIn {
AppSkeleton()
}
else {
LoginScreen()
}
}
}
struct Router_Previews: PreviewProvider {
static var previews: some View {
Router()
}
}

View File

@ -1,5 +1,5 @@
//
// RootView.swift
// AppSkeleton.swift
// Music Tools
//
// Created by Andy Pack on 16/02/2020.
@ -11,12 +11,11 @@ import SwiftUIRefresh
import Alamofire
import SwiftyJSON
struct RootView: View {
struct AppSkeleton: View {
@EnvironmentObject var liveUser: LiveUser
@State private var selection = 0 // Tab view selection
@State private var showAdd = false // State for showing add modal view
var body: some View {
@ -114,7 +113,7 @@ struct RootView: View {
}
.tabItem {
VStack {
Image(systemName: "sum")
Image(systemName: "tag")
Text("Tags")
}
}
@ -144,6 +143,6 @@ struct RootView: View {
struct RootView_Previews: PreviewProvider {
static var previews: some View {
RootView()
AppSkeleton()
}
}

View File

@ -9,9 +9,15 @@
import SwiftUI
struct LoginScreen: View {
@EnvironmentObject var liveUser: LiveUser
@State private var username: String = ""
@State private var password: String = ""
var body: some View {
VStack {
Image("Logo")
Image("MusicToolsLogo")
.resizable()
.frame(width: 200.0, height: 200.0, alignment: .trailing)
.cornerRadius(18)
@ -19,12 +25,13 @@ struct LoginScreen: View {
.padding(.bottom, 20)
Text("Sarsoo Music Tools")
.font(.largeTitle)
TextField("Username", text: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Value@*/.constant("")/*@END_MENU_TOKEN@*/)
SecureField("Password", text: /*@START_MENU_TOKEN@*/ /*@PLACEHOLDER=Value@*/.constant("Apple")/*@END_MENU_TOKEN@*/)
TextField("Username", text: $username)
SecureField("Password", text: $password)
HStack {
Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/) {
Button(action: {
self.liveUser.loggedIn = true
}) {
Text("Log In")
}
.padding(.trailing, 20.0)
Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/) {

View File

@ -23,7 +23,6 @@ struct PlaylistView: View {
@State private var chart_limit: Int = 0
@State private var showingSheet = false
@State private var isRefreshing = false
var chartStyle: ChartStyle {

View File

@ -11,6 +11,8 @@ import KeychainAccess
struct SettingsList: View {
@EnvironmentObject var liveUser: LiveUser
init(){
UITableView.appearance().tableFooterView = UIView()
}
@ -31,6 +33,9 @@ struct SettingsList: View {
do {
try keychain.remove("username")
try keychain.remove("password")
self.liveUser.loggedIn = false
} catch let error {
debugPrint("Could not clear keychain, \(error)")
}