Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion SwiftSimctlExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
3340A8692722117000C4CBA5 /* Example_03_OpenUrlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3340A8682722116F00C4CBA5 /* Example_03_OpenUrlTests.swift */; };
F830E0C4242BA5B900875D0A /* Example_02_ChangeAppearanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830E0C3242BA5B900875D0A /* Example_02_ChangeAppearanceTests.swift */; };
F830E0C6242BA5E400875D0A /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830E0C5242BA5E400875D0A /* Shared.swift */; };
F851FF952423ADB20099A319 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F851FF942423ADB20099A319 /* AppDelegate.swift */; };
Expand All @@ -31,6 +32,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
3340A8682722116F00C4CBA5 /* Example_03_OpenUrlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Example_03_OpenUrlTests.swift; sourceTree = "<group>"; };
F830E0C3242BA5B900875D0A /* Example_02_ChangeAppearanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example_02_ChangeAppearanceTests.swift; sourceTree = "<group>"; };
F830E0C5242BA5E400875D0A /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = "<group>"; };
F851FF912423ADB20099A319 /* SwiftSimctlExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSimctlExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -118,6 +120,7 @@
F830E0C5242BA5E400875D0A /* Shared.swift */,
F851FFB62423ADB30099A319 /* Example_01_HandlePushNotificationsTests.swift */,
F830E0C3242BA5B900875D0A /* Example_02_ChangeAppearanceTests.swift */,
3340A8682722116F00C4CBA5 /* Example_03_OpenUrlTests.swift */,
);
path = SwiftSimctlExampleUITests;
sourceTree = "<group>";
Expand Down Expand Up @@ -251,6 +254,7 @@
buildActionMask = 2147483647;
files = (
F830E0C6242BA5E400875D0A /* Shared.swift in Sources */,
3340A8692722117000C4CBA5 /* Example_03_OpenUrlTests.swift in Sources */,
F851FFB72423ADB30099A319 /* Example_01_HandlePushNotificationsTests.swift in Sources */,
F830E0C4242BA5B900875D0A /* Example_02_ChangeAppearanceTests.swift in Sources */,
);
Expand Down Expand Up @@ -524,7 +528,7 @@
repositoryURL = "https://github.com/ctreffs/SwiftSimctl.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.3.0;
minimumVersion = 0.4.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down

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

16 changes: 16 additions & 0 deletions SwiftSimctlExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI

struct ContentView: View {
@State private var authorizationStatus: UNAuthorizationStatus = .notDetermined
@State private var deepLinkPath: String = "none"

var body: some View {
VStack {
Expand All @@ -25,8 +26,19 @@ struct ContentView: View {
} else if authorizationStatus == .denied {
Button(action: { UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) }) { Text("Re-enable push authorization manually in settings") }
}

Text("Deep link path:")
.fontWeight(.bold)
.padding(4)
Text(deepLinkPath)
.foregroundColor(deepLinkPath == "none" ? .gray : .blue)
.padding(20)
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in self.updateAuthorizationStatus() }
.onReceive(NotificationCenter.default.publisher(for: .deepLink)) { notification in
guard let deepLinkPath = notification.object as? String else { return }
self.deepLinkPath = deepLinkPath
}
.onAppear(perform: { self.updateAuthorizationStatus() })
}

Expand Down Expand Up @@ -73,12 +85,16 @@ extension UNAuthorizationStatus: CustomStringConvertible {
switch self {
case .authorized:
return .green

case .notDetermined:
return .gray

case .denied:
return .red

case .ephemeral:
return .blue

case .provisional:
return .yellow
@unknown default:
Expand Down
13 changes: 13 additions & 0 deletions SwiftSimctlExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>CFBundleURLName</key>
<string>com.swiftsimctl.example</string>
<key>CFBundleURLSchemes</key>
<array>
<string>swiftsimctlexample</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
Expand Down
20 changes: 20 additions & 0 deletions SwiftSimctlExample/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,24 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window.makeKeyAndVisible()
}
}

/// If the app is opened with a url using the registered scheme `swiftsimctlexample`
/// then this code will post a deep link notification whose object is url host + path.
///
/// Example:
/// - Opening `swiftsimctlexample://foo/bar/baz` posts a Notification
/// named `"deepLink"` with String object `"foo/bar/baz"`.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if
let url = URLContexts.first?.url,
url.scheme?.lowercased() == "swiftsimctlexample"
{
let path = "\(url.host ?? "")\(url.path)"
NotificationCenter.default.post(name: .deepLink, object: path)
}
}
}

extension Notification.Name {
static let deepLink: Notification.Name = .init(rawValue: "deepLink")
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import Simctl
import XCTest

class Example_01_HandlePushNotificationsTests: XCTestCase {
lazy var simctl = SimctlClient(SimulatorEnvironment(bundleIdentifier: exampleAppBundleId,
host: .localhost(port: 8080))!)

func testAllowPushNotifications() {
// MARK: Launch app and enable push notifications
let app = XCUIApplication.exampleApp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import SnapshotTesting
import XCTest

class Example_02_ChangeAppearanceTests: XCTestCase {
lazy var simctl = SimctlClient(SimulatorEnvironment(bundleIdentifier: exampleAppBundleId,
host: .localhost(port: 8080))!)
// run on iPhone 11 Pro
func testDifferentAppearances() throws {
let app = XCUIApplication.exampleApp
Expand Down
50 changes: 50 additions & 0 deletions SwiftSimctlExampleUITests/Example_03_OpenUrlTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Example_03_OpenUrlTests.swift
// SwiftSimctlExampleUITests
//
// Created by Robert Clark on 21.10.21.
// Copyright © 2021 Christian Treffs. All rights reserved.
//

import Simctl
import XCTest

class Example_03_OpenUrlTests: XCTestCase {
func testOpenUrlWithRegisteredScheme() throws {
let app = XCUIApplication.exampleApp
app.launch()

let exp = expectation(description: "\(#function)")

let deepLinkUrl = URL(string: "swiftsimctlexample://myDeepLink/display")!

simctl.openUrl(deepLinkUrl) { result in
switch result {
case .success:
exp.fulfill()

case let .failure(error):
XCTFail("\(error)")
}
}

wait(for: [exp], timeout: 5.0)

// First installs on fresh simulators will prompt to open registered urls.
// UIInterruptionMonitor doesn't intercept this dialog (as of iOS 15
// & Xcode 13) so we use Springboard to tap on the potential positive dialog
// button instead.
let openButton = XCUIApplication.springboard.buttons.element(boundBy: 1)
if openButton.waitForExistence(timeout: 2) {
openButton.tap()
}

let deepLinkPathLabel = app.staticTexts["myDeepLink/display"]
guard deepLinkPathLabel.waitForExistence(timeout: 5.0) else {
XCTFail("We did not find deep link path label")
return
}

XCTAssertEqual(deepLinkPathLabel.label, "myDeepLink/display")
}
}
8 changes: 8 additions & 0 deletions SwiftSimctlExampleUITests/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
// Copyright © 2020 Christian Treffs. All rights reserved.
//

import Simctl
import XCTest

let exampleAppBundleId = "com.example.SwiftSimctlExample"

let simctl = SimctlClient(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to Shared.swift for convenience, plus it remains lazy as it's a global property.

SimulatorEnvironment(
bundleIdentifier: exampleAppBundleId,
host: .localhost(port: 8080)
)!
)

extension XCUIApplication {
static let exampleApp = XCUIApplication(bundleIdentifier: exampleAppBundleId)
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.