Skip to content
This repository has been archived by the owner on Feb 12, 2021. It is now read-only.

Commit

Permalink
🎉 Initial iOS 14 release
Browse files Browse the repository at this point in the history
  • Loading branch information
rrroyal committed Oct 31, 2020
0 parents commit 30753c0
Show file tree
Hide file tree
Showing 207 changed files with 16,776 additions and 0 deletions.
54 changes: 54 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Xcode & macOS

# OS X temporary files that should never be committed
.DS_Store
*.swp
*.lock
profile

# Xcode temporary files that should never be committed
*~.nib

# Xcode build files
DerivedData/
build/
.build/

# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
xcuserdata/**/*

# Cocoapods: cocoapods.org
Pods/
Podfile.lock

## Obj-C/Swift specific
*.hmap

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
Packages/
Package.pins
Package.resolved

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Modules/AppNotifications/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
32 changes: 32 additions & 0 deletions Modules/AppNotifications/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "AppNotifications",
platforms: [
.iOS(.v14),
.macOS(.v11)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "AppNotifications",
targets: ["AppNotifications"]),
],
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 this package depends on.
.target(
name: "AppNotifications",
dependencies: []),
.testTarget(
name: "AppNotificationsTests",
dependencies: ["AppNotifications"]),
]
)
6 changes: 6 additions & 0 deletions Modules/AppNotifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# AppNotifications

App notifications overlay.

## Usage
Add the `.appNotificationOverlay()` modifier to the selected view (i.e. root app view).
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// AppNotifications.swift
//
//
// Created by royal on 10/07/2020.
//

#if !WIDGET
import Foundation
import SwiftUI
import Combine

@available (iOS 14, macOS 10.16, watchOS 7, tvOS 14, *)
public final class AppNotifications: ObservableObject {
public static let shared: AppNotifications = AppNotifications()

@Published public var notification: NotificationData? = nil
@Published public var isPresented: Bool = false

private var timer: AnyCancellable? = nil
private var cancellableSet: Set<AnyCancellable> = []

private init() {
$notification
.sink { notification in
if notification == nil {
self.isPresented = false
return
}

self.isPresented = true
self.instantiateTimer()
}
.store(in: &cancellableSet)
}

public func instantiateTimer() {
self.timer?.cancel()
self.timer = Timer.publish(every: 5, on: .main, in: .common)
.autoconnect()
.sink { _ in
self.notification = nil
self.isPresented = false
self.timer?.cancel()
}
}

public func cancelTimer() {
self.timer?.cancel()
}
}

extension AppNotifications {
public struct NotificationData: Identifiable, Equatable {
public enum NotificationStyle {
case normal
case information
case warning
case error
case success
}

public init(id: UUID = UUID(), autodismisses: Bool, dismissable: Bool, style: NotificationStyle, icon: String, title: String, subtitle: String, expandedText: String? = nil) {
self.id = id
self.autodismisses = autodismisses
self.dismissable = dismissable
self.style = style
self.icon = icon
self.title = title
self.subtitle = subtitle
self.expandedText = expandedText
}

public init(error: String) {
self.init(autodismisses: true, dismissable: true, style: .error, icon: "exclamationmark.triangle.fill", title: "Error!", subtitle: error)
}

public var id: UUID = UUID()
var autodismisses: Bool
var dismissable: Bool
var style: NotificationStyle
var icon: String
var title: String
var subtitle: String
var expandedText: String?

var primaryColor: Color {
switch (self.style) {
case .normal: return .primary
case .information: return .blue
case .warning: return .orange
case .error: return .red
case .success: return .green
}
}

var backgroundColor: Color {
switch (self.style) {
#if os(macOS)
case .normal: return Color(NSColor.controlBackgroundColor)
#else
case .normal: return Color(UIColor.tertiarySystemBackground)
#endif
case .information: return Color.blue.opacity(0.1)
case .warning: return Color.orange.opacity(0.1)
case .error: return Color.red.opacity(0.1)
case .success: return Color.green.opacity(0.1)
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//
// NotificationsOverlay.swift
//
//
// Created by royal on 10/07/2020.
//

#if !WIDGET
import SwiftUI
import Combine
import os

#if os(iOS)
import CoreHaptics
#endif

@available (iOS 14, watchOS 7, macOS 10.16, tvOS 14, *)
public extension View {
func notificationOverlay(_ appNotifications: AppNotifications) -> some View {
return self.overlay(NotificationOverlay(appNotifications: appNotifications))
}
}

/// Overlays a notification on top of the views
@available (iOS 14, watchOS 7, macOS 10.16, tvOS 14, *)
public struct NotificationOverlay: View {
@ObservedObject var appNotifications: AppNotifications

@State private var isExpanded: Bool = false
@State private var translation = CGSize.zero

private var yOffset: CGFloat {
if translation.height > 0 {
return translation.height * 0.075
}

return translation.height
}

private var notificationDragGesture: some Gesture {
DragGesture()
.onChanged { value in
translation = value.translation
}
.onEnded { value in
if value.translation.height < -20 {
appNotifications.isPresented = false
}
translation = CGSize.zero
}
}

@ViewBuilder var notificationContent: some View {
if let notification = appNotifications.notification {
VStack(alignment: .leading) {
HStack {
Image(systemName: notification.icon)
.font(.system(size: 26, weight: .bold, design: .default))
.id(notification.icon)
.foregroundColor(notification.primaryColor)

VStack(alignment: .leading) {
Text(LocalizedStringKey(notification.title))
.font(.headline)
.lineLimit(2)
.id(notification.title)
.foregroundColor(notification.primaryColor)

Text(LocalizedStringKey(notification.subtitle))
.font(.headline)
.opacity(0.5)
.lineLimit(3)
.id(notification.subtitle)
.foregroundColor(notification.primaryColor)
}
.padding(.horizontal, 5)

Spacer()
}

if let expandedText = notification.expandedText,
isExpanded {
VStack {
Text(LocalizedStringKey(expandedText))
.font(.headline)
.multilineTextAlignment(.leading)
.lineLimit(nil)
.foregroundColor(notification.primaryColor)
.opacity(0.75)
}
.transition(.opacity)
.padding(.top, 5)
.onAppear {
appNotifications.cancelTimer()
}
.onDisappear {
if notification.autodismisses {
appNotifications.instantiateTimer()
}
}
}
}
.padding()
.padding(.vertical, 5)
.background(notification.backgroundColor)
.background(Color(UIColor.systemBackground))
.mask(RoundedRectangle(cornerRadius: 14, style: .circular))
.gesture(notificationDragGesture)
.onTapGesture {
if notification.expandedText != nil {
#if os(iOS)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
#endif
isExpanded.toggle()
if notification.autodismisses {
if isExpanded {
appNotifications.cancelTimer()
} else {
appNotifications.instantiateTimer()
}
}
}
}
.shadow(color: Color.black.opacity(0.1), radius: 20, x: 0, y: 0)
.offset(x: 0, y: appNotifications.isPresented ? yOffset : -200)
.animation(.interpolatingSpring(mass: 0.5, stiffness: 45, damping: 45, initialVelocity: 15))
.transition(.asymmetric(insertion: .move(edge: .top), removal: .offset(x: 0, y: -200)))
}
}

public var body: some View {
VStack {
notificationContent

Spacer()
}
.padding()
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import XCTest
@testable import AppNotifications

final class AppNotificationsTests: 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(AppNotifications().text, "Hello, World!")
}

static var allTests = [
("testExample", testExample),
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import XCTest

#if !canImport(ObjectiveC)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(AppNotificationsTests.allTests),
]
}
#endif
7 changes: 7 additions & 0 deletions Modules/AppNotifications/Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import XCTest

import AppNotificationsTests

var tests = [XCTestCaseEntry]()
tests += AppNotificationsTests.allTests()
XCTMain(tests)
5 changes: 5 additions & 0 deletions Modules/Vulcan/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
Loading

0 comments on commit 30753c0

Please sign in to comment.