Skip to content

Commit

Permalink
Replace the method to show SFSafariViewController (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
touyou authored Mar 21, 2024
1 parent 8feac26 commit ba95e2e
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 194 deletions.
21 changes: 11 additions & 10 deletions MyLibrary/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-composable-architecture", from: "1.9.1"),
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.2.0"),
.package(url: "https://github.com/zunda-pixel/LicenseProvider", from: "1.1.1"),
],
targets: [
Expand All @@ -39,11 +40,17 @@ let package = Package(
.process("Resources")
]
),
.target(
name: "DependencyExtra",
dependencies: [
.product(name: "Dependencies", package: "swift-dependencies"),
]
),
.target(
name: "GuidanceFeature",
dependencies: [
"DependencyExtra",
"MapKitClient",
"Safari",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
),
Expand All @@ -54,17 +61,11 @@ let package = Package(
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
),
.target(
name: "Safari",
dependencies: [
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
]
),
.target(
name: "ScheduleFeature",
dependencies: [
"DataClient",
"Safari",
"DependencyExtra",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
),
Expand All @@ -73,15 +74,15 @@ let package = Package(
name: "SponsorFeature",
dependencies: [
"DataClient",
"Safari",
"DependencyExtra",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
),
.target(
name: "trySwiftFeature",
dependencies: [
"DataClient",
"Safari",
"DependencyExtra",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
],
plugins: [
Expand Down
82 changes: 82 additions & 0 deletions MyLibrary/Sources/DependencyExtra/Safari.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Dependencies

#if canImport(SafariServices) && canImport(SwiftUI)
import SafariServices
import SwiftUI

extension DependencyValues {
/// A dependency that opens a URL in SFSafariViewController.
///
/// In iOS, use SFSafariViewController in UIKit context. Otherwise use openURL in environment values
///
/// - SeeAlso: https://sarunw.com/posts/sfsafariviewcontroller-in-swiftui/
@available(iOS 15, macOS 11, tvOS 14, watchOS 7, *)
public var safari: SafariEffect {
get { self[SafariKey.self] }
set { self[SafariKey.self] = newValue }
}
}

@available(iOS 15, macOS 11, tvOS 14, watchOS 7, *)
private enum SafariKey: DependencyKey {
static let liveValue = SafariEffect { url in
let stream = AsyncStream<Bool> { continuation in
let task = Task { @MainActor in
#if os(iOS)
let vc = SFSafariViewController(url: url)
UIApplication.shared.firstKeyWindow?.rootViewController?.present(vc, animated: true)
continuation.yield(true)
continuation.finish()
#else
EnvironmentValues().openURL(url)
continuation.yield(true)
continuation.finish()
#endif
}
continuation.onTermination = { @Sendable _ in
task.cancel()
}
}
return await stream.first(where: { _ in true }) ?? false
}
static let testValue = SafariEffect { _ in
XCTFail(#"Unimplemented: @Dependency(\.safari)"#)
return false
}
}

public struct SafariEffect: Sendable {
private let handler: @Sendable (URL) async -> Bool

public init(handler: @escaping @Sendable (URL) async -> Bool) {
self.handler = handler
}

@available(watchOS, unavailable)
@discardableResult
public func callAsFunction(_ url: URL) async -> Bool {
await self.handler(url)
}

@_disfavoredOverload
public func callAsFunction(_ url: URL) async {
_ = await self.handler(url)
}
}

#endif

#if canImport(UIKit)
import UIKit

extension UIApplication {
@available(iOS 15.0, *)
var firstKeyWindow: UIWindow? {
return UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.activationState == .foregroundActive }
.first?.keyWindow
}
}

#endif
4 changes: 2 additions & 2 deletions MyLibrary/Sources/GuidanceFeature/Guidance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import CoreLocation
import Foundation
import MapKit
import MapKitClient
import Safari
import DependencyExtra
import SwiftUI

@Reducer
Expand Down Expand Up @@ -49,10 +49,10 @@ public struct Guidance {

@Reducer(state: .equatable)
public enum Destination {
case safari(Safari)
}

@Dependency(MapKitClient.self) var mapKitClient
@Dependency(\.safari) var safari

public init() {}

Expand Down
53 changes: 0 additions & 53 deletions MyLibrary/Sources/Safari/Safari.swift

This file was deleted.

26 changes: 3 additions & 23 deletions MyLibrary/Sources/ScheduleFeature/Detail.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ComposableArchitecture
import Foundation
import Safari
import DependencyExtra
import SharedModels
import SwiftUI

Expand All @@ -13,7 +13,6 @@ public struct ScheduleDetail {
var description: String
var requirements: String?
var speakers: [Speaker]
@Presents var destination: Destination.State?

public init(
title: String, description: String, requirements: String? = nil, speakers: [Speaker]
Expand All @@ -27,20 +26,14 @@ public struct ScheduleDetail {

public enum Action: ViewAction, BindableAction {
case binding(BindingAction<State>)
case destination(PresentationAction<Destination.Action>)
case view(View)

public enum View {
case snsTapped(URL)
}
}

@Reducer(state: .equatable)
public enum Destination {
case safari(Safari)
}

@Dependency(\.openURL) var openURL
@Dependency(\.safari) var safari

public init() {}

Expand All @@ -49,19 +42,11 @@ public struct ScheduleDetail {
Reduce { state, action in
switch action {
case let .view(.snsTapped(url)):
#if os(iOS) || os(macOS)
state.destination = .safari(.init(url: url))
return .none
#elseif os(visionOS)
return .run { _ in await openURL(url) }
#endif
case .destination:
return .none
return .run { _ in await safari(url) }
case .binding:
return .none
}
}
.ifLet(\.$destination, action: \.destination)
}
}

Expand Down Expand Up @@ -103,11 +88,6 @@ public struct ScheduleDetailView: View {
speakers
.frame(maxWidth: 700) // Readable content width for iPad
}
.sheet(item: $store.scope(state: \.destination?.safari, action: \.destination.safari)) {
sheetStore in
SafariViewRepresentation(url: sheetStore.url)
.ignoresSafeArea()
}
}

@ViewBuilder
Expand Down
2 changes: 0 additions & 2 deletions MyLibrary/Sources/ScheduleFeature/Schedule.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ComposableArchitecture
import DataClient
import Foundation
import Safari
import SharedModels
import SwiftUI
import TipKit
Expand Down Expand Up @@ -59,7 +58,6 @@ public struct Schedule {
public enum Destination {}

@Dependency(DataClient.self) var dataClient
@Dependency(\.openURL) var openURL

public init() {}

Expand Down
18 changes: 3 additions & 15 deletions MyLibrary/Sources/SponsorFeature/Sponsors.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ComposableArchitecture
import DataClient
import Foundation
import Safari
import DependencyExtra
import SharedModels
import SwiftUI

Expand Down Expand Up @@ -32,7 +32,7 @@ public struct SponsorsList {
public init() {}

@Dependency(DataClient.self) var dataClient
@Dependency(\.openURL) var openURL
@Dependency(\.safari) var safari

public var body: some ReducerOf<Self> {
BindingReducer()
Expand All @@ -44,12 +44,7 @@ public struct SponsorsList {

case let .view(.sponsorTapped(sponsor)):
guard let url = sponsor.link else { return .none }
#if os(iOS) || os(macOS)
state.destination = .safari(.init(url: url))
return .none
#elseif os(visionOS)
return .run { _ in await openURL(url) }
#endif
return .run { _ in await safari(url) }
case .binding:
return .none
case .destination:
Expand All @@ -61,7 +56,6 @@ public struct SponsorsList {

@Reducer(state: .equatable)
public enum Destination {
case safari(Safari)
}
}

Expand All @@ -76,12 +70,6 @@ public struct SponsorsListView: View {
public var body: some View {
NavigationView {
root
.fullScreenCover(
item: $store.scope(state: \.destination?.safari, action: \.destination.safari)
) { sheetStore in
SafariViewRepresentation(url: sheetStore.url)
.ignoresSafeArea()
}
.onAppear {
send(.onAppear)
}
Expand Down
Loading

0 comments on commit ba95e2e

Please sign in to comment.