Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2f7ea30
Renamed LocalLoginViewModel.swift LoginViewModel.swift
brentvegafederal Jul 28, 2025
5fbb1ba
Refactored filenames
brentvegafederal Jul 28, 2025
4c2a7ec
Refactoring for better code reuse.
brentvegafederal Jul 29, 2025
33ea27c
Merge remote-tracking branch 'origin/develop' into Brent/Login_View_M…
brentvegafederal Jul 31, 2025
9d9b21e
Changed the placeholder string's default to the correct value.
brentvegafederal Jul 31, 2025
1872ac5
Cleaned up code a bit.
brentvegafederal Jul 31, 2025
0d2f133
Working on adding the IDP login view and getting rid of the old files…
brentvegafederal Jul 31, 2025
7b3e3d4
Updated to proper type
brentvegafederal Aug 4, 2025
fd619ba
Commented out LdapLoginView and got everything building again.
brentvegafederal Aug 4, 2025
2113173
Removed AuthenticationButtonDelegate and updated code to use IDPButto…
brentvegafederal Aug 4, 2025
abdc071
Fixed protocol issues caused by refactoring
brentvegafederal Aug 4, 2025
b02965f
Added LoginRootViewSwiftUI but it is on hold and currently commented …
brentvegafederal Aug 4, 2025
69b1931
Removed references to files recently deleted.
brentvegafederal Aug 4, 2025
10aa90a
Fixed the name of IDPCoordinator in the header
brentvegafederal Aug 5, 2025
9046e9c
Created first tests for the LoginViewModel
brentvegafederal Aug 5, 2025
380a920
working on trying to get ViewInspector updated from released v0.10.2 …
brentvegafederal Aug 5, 2025
2d1ff19
Added several more tests
brentvegafederal Aug 5, 2025
38ee001
Added more tests and a "demo" view to see all types of authenication
brentvegafederal Aug 5, 2025
39fea22
Added 1 more test to show multiple ways
brentvegafederal Aug 5, 2025
1b89107
Did a bit more cleanup
brentvegafederal Aug 5, 2025
472c5e5
Cleaning up code and testing strings
brentvegafederal Aug 5, 2025
33b50f0
Finishing the cleanup of testing strings.
brentvegafederal Aug 5, 2025
e2fca54
Merge branch 'develop' into Brent/Login_View_Modernization
brentvegafederal Aug 6, 2025
9dd7699
Updated to fix some merge conflicts
brentvegafederal Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 87 additions & 28 deletions MAGE.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions MAGE.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"FeedItemsViewControllerNoTimestampTests\/testOneFeedItemNoContent()",
"FeedItemsViewControllerWithTimestampTests",
"FeedItemsViewControllerWithTimestampTests\/testOneFeedItemWithPrimaryAndSecondaryValueAndIconWithoutTimestamp()",
"LocalLoginViewTests",
"MageServerTestsSwift\/testShouldFailWhenAnImageIsrReturned()",
"MageServiceTests",
"MageTests",
Expand Down
15 changes: 12 additions & 3 deletions MAGE.xcworkspace/xcshareddata/swiftpm/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions Mage/AuthenticationButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@
//

#import <Foundation/Foundation.h>
#import "MAGE-Swift.h"

@import MaterialComponents;

NS_ASSUME_NONNULL_BEGIN

@protocol AuthenticationButtonDelegate

- (void) onAuthenticationButtonTapped:(id) sender;

@end

@interface AuthenticationButton : UIView

@property (strong, nonatomic) NSDictionary *strategy;
@property (weak, nonatomic) id<AuthenticationButtonDelegate> delegate;
@property (weak, nonatomic) id<IDPLoginDelegate> delegate;

- (void) applyThemeWithContainerScheme:(id<MDCContainerScheming>)containerScheme;

Expand Down
5 changes: 0 additions & 5 deletions Mage/AuthenticationButton.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ - (void) applyThemeWithContainerScheme:(id<MDCContainerScheming>)containerScheme
[self setStrategy:self.strategy];
}


- (IBAction)onAuthenticationButtonTapped:(id)sender {
[self.delegate onAuthenticationButtonTapped:sender];
}

-(void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self setButtonBackgroundColor:self.buttonColorHighlighted];
Expand Down
14 changes: 4 additions & 10 deletions Mage/AuthenticationButton.xib
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand Down Expand Up @@ -79,10 +77,6 @@
</connections>
<point key="canvasLocation" x="76.811594202898561" y="-102.45535714285714"/>
</view>
<tapGestureRecognizer id="lU4-a0-QMb">
<connections>
<action selector="onAuthenticationButtonTapped:" destination="-1" id="Nrq-yQ-13g"/>
</connections>
</tapGestureRecognizer>
<tapGestureRecognizer id="lU4-a0-QMb"/>
</objects>
</document>
3 changes: 1 addition & 2 deletions Mage/AuthenticationCoordinator.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#import "LoginViewController.h"
#import "SignUpViewController.h"
#import "SignUpViewController_Server5.h"
#import "IDPLoginView.h"
#import "IDPCoordinator.h"
#import "MageOfflineObservationManager.h"
#import "FadeTransitionSegue.h"
Expand All @@ -21,7 +20,7 @@
#import "ContactInfo.h"
#import "MAGE-Swift.h"

@interface AuthenticationCoordinator() <LoginDelegate, DisclaimerDelegate, SignupDelegate, IDPButtonDelegate>
@interface AuthenticationCoordinator() <LoginDelegate, DisclaimerDelegate, SignupDelegate, IDPLoginDelegate>

@property (strong, nonatomic) UINavigationController *navigationController;
@property (strong, nonatomic, readwrite) MageServer *server;
Expand Down
1 change: 0 additions & 1 deletion Mage/AuthenticationCoordinator_Server5.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#import "AuthenticationCoordinator_Server5.h"
#import "LoginViewController.h"
#import "SignUpViewController.h"
#import "IDPLoginView.h"
#import "IDPCoordinator.h"
#import "MAGE-Swift.h"
#import "MageOfflineObservationManager.h"
Expand Down
42 changes: 42 additions & 0 deletions Mage/DemoAllLoginViews.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// DemoAllLoginViews.swift
// MAGETests
//
// Created by Brent Michalski on 8/5/25.
// Copyright © 2025 National Geospatial Intelligence Agency. All rights reserved.
//

import Foundation
import SwiftUI

let demoStrategies: [[String: Any]] = [
["identifier": "local", "strategy": ["title": "Email"]],
["identifier": "ldap", "strategy": ["title": "LDAP"]],
["identifier": "idp", "strategy": ["title": "IDP", "name": "SSO"]]
]

struct DemoAllLoginViews: View {
var body: some View {
VStack(spacing: 24) {
ForEach(Array(demoStrategies.enumerated()), id: \.offset) { _, strategy in
if let identifier = strategy["identifier"] as? String {
if identifier == "local" || identifier == "ldap" {
LoginViewSwiftUI(viewModel: LoginViewModel(strategy: strategy, delegate: nil))
.background(Color(UIColor.secondarySystemBackground))
.cornerRadius(12)
.padding()
} else if identifier == "idp" {
IDPLoginViewSwiftUI(viewModel: IDPLoginViewModel(strategy: strategy, delegate: nil))
.padding()
}
}
}
}
.padding()
}
}

#Preview("All Strategies Demo") {
DemoAllLoginViews()
}

2 changes: 1 addition & 1 deletion Mage/IDPCoordinator.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// OAuthCoordinator.h
// IDPCoordinator.h
// MAGE
//
// Created by William Newman on 5/18/20.
Expand Down
25 changes: 0 additions & 25 deletions Mage/IDPLoginView.h

This file was deleted.

35 changes: 0 additions & 35 deletions Mage/IDPLoginView.m

This file was deleted.

31 changes: 31 additions & 0 deletions Mage/IDPLoginViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// IDPLoginViewModel.swift
// MAGE
//
// Created by Brent Michalski on 7/31/25.
// Copyright © 2025 National Geospatial Intelligence Agency. All rights reserved.
//

import Foundation
import Combine

@objcMembers
public class IDPLoginViewModel: NSObject, ObservableObject {
let strategy: [String: Any]
weak var delegate: IDPLoginDelegate?

init(strategy: [String: Any], delegate: IDPLoginDelegate?) {
self.strategy = strategy
self.delegate = delegate
}

func signin() {
delegate?.signinForStrategy(strategy as NSDictionary)
}

// Properties for button and such, based on strategy
var displayName: String {
strategy["name"] as? String ?? "Sign in with IDP"
}
}

31 changes: 31 additions & 0 deletions Mage/IDPLoginViewModelWrapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// IDPLoginViewModelWrapper.swift
// MAGE
//
// Created by Brent Michalski on 7/31/25.
// Copyright © 2025 National Geospatial Intelligence Agency. All rights reserved.
//

import Foundation
import SwiftUI

@objc public protocol IDPLoginDelegate: NSObjectProtocol {
@objc func signinForStrategy(_ strategy: NSDictionary)
}

@objc public class IDPLoginViewModelWrapper: NSObject {
@objc public var viewModel: IDPLoginViewModel

@objc public init(strategy: NSDictionary, delegate: IDPLoginDelegate?) {
let swiftStrategy = strategy as? [String: Any] ?? [:]
self.viewModel = IDPLoginViewModel(strategy: swiftStrategy, delegate: delegate)
super.init()
}
}

@objc public class IDPLoginViewHoster: NSObject {
@objc public static func hostingController(withViewModel viewModel: IDPLoginViewModel) -> UIViewController {
let swiftUIView = IDPLoginViewSwiftUI(viewModel: viewModel)
return UIHostingController(rootView: swiftUIView)
}
}
53 changes: 53 additions & 0 deletions Mage/IDPLoginViewSwiftUI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// IDPLoginViewSwiftUI.swift
// MAGE
//
// Created by Brent Michalski on 7/31/25.
// Copyright © 2025 National Geospatial Intelligence Agency. All rights reserved.
//

import SwiftUI

struct IDPLoginViewSwiftUI: View {
@ObservedObject var viewModel: IDPLoginViewModel

var body: some View {
Button(action: {
viewModel.signin()
}) {
HStack {
Image(systemName: "person.crop.circle.badge.checkmark")
Text(viewModel.displayName)
.font(.headline)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.padding(.vertical, 12)
}
}


// MARK: - Mock Delegate for Preview
class MockIDPLoginDelegate: NSObject, IDPLoginDelegate {
func signinForStrategy(_ strategy: NSDictionary) {
print("Sign in tapped for strategy: \(strategy)")
}
}


// MARK: - Preview
struct IDPLoginViewSwiftUI_Previews: PreviewProvider {
static var previews: some View {
let mockStrategy: [String: Any] = [
"identifier": "idp",
"name": "Sign in with Google",
"url": "https://example.com/oauth"
]
let mockDelegate = MockIDPLoginDelegate()
let viewModel = IDPLoginViewModel(strategy: mockStrategy, delegate: mockDelegate)
return IDPLoginViewSwiftUI(viewModel: viewModel)
.padding()
.previewLayout(.sizeThatFits)
}
}
10 changes: 5 additions & 5 deletions Mage/IntroViews/Views/MageIntroButtonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ struct MageIntroButtonView: View {
struct MageIntroButtonView_Previews: PreviewProvider {
static var previews: some View {
Group {
LocalLoginViewSwiftUI(viewModel: PreviewLocalLoginViewModel())
LoginViewSwiftUI(viewModel: PreviewLoginViewModel())
.previewDisplayName("Default")
LocalLoginViewSwiftUI(viewModel: {
let vm = PreviewLocalLoginViewModel()
LoginViewSwiftUI(viewModel: {
let vm = PreviewLoginViewModel()
vm.errorMessage = "Bad username or password!"
return vm
}())
.previewDisplayName("With Error")
LocalLoginViewSwiftUI(viewModel: {
let vm = PreviewLocalLoginViewModel()
LoginViewSwiftUI(viewModel: {
let vm = PreviewLoginViewModel()
vm.isLoading = true
return vm
}())
Expand Down
Loading