Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
jurajhilje committed Sep 17, 2024
2 parents 55b4ef5 + 127f762 commit 2e1b8e8
Show file tree
Hide file tree
Showing 27 changed files with 474 additions and 155 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

All notable changes to this project will be documented in this file.

## 2.12.4 - 2024-09-17

[NEW] Option to show/hide Account ID
[IMPROVED] Error handling for in-app purchases
[FIXED] Location marker disappearing when the app wakes up from the background (iOS 18)
[FIXED] Privacy overlay not working (iOS 18)
[FIXED] Widget actions to Connect/Disconnect not working (iOS 18)
[FIXED] Actions not appearing in the Shortcuts app (iOS 18)

## 2.12.3 - 2024-06-04

[IMPROVED] Post-Quantum library updated to the latest version
Expand Down
62 changes: 40 additions & 22 deletions IVPNClient.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions IVPNClient/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,9 @@ class AppDelegate: UIResponder {

private func showSecurityScreen() {
var showWindow = false
let topVC = UIApplication.topViewController()

if UIApplication.topViewController() as? AccountViewController != nil {
showWindow = true
}

if UIApplication.topViewController() as? LoginViewController != nil {
showWindow = true
}

if UIApplication.topViewController() as? CreateAccountViewController != nil {
if topVC is AccountViewController || topVC is LoginViewController || topVC is CreateAccountViewController {
showWindow = true
}

Expand Down Expand Up @@ -439,3 +432,12 @@ extension AppDelegate: PurchaseManagerDelegate {
}

}

enum UserActivityType {
static let Connect = "net.ivpn.clients.ios.Connect"
static let Disconnect = "net.ivpn.clients.ios.Disconnect"
static let AntiTrackerEnable = "net.ivpn.clients.ios.AntiTracker.enable"
static let AntiTrackerDisable = "net.ivpn.clients.ios.AntiTracker.disable"
static let CustomDNSEnable = "net.ivpn.clients.ios.CustomDNS.enable"
static let CustomDNSDisable = "net.ivpn.clients.ios.CustomDNS.disable"
}
33 changes: 0 additions & 33 deletions IVPNClient/Enums/UserActivityTitle.swift

This file was deleted.

33 changes: 0 additions & 33 deletions IVPNClient/Enums/UserActivityType.swift

This file was deleted.

13 changes: 2 additions & 11 deletions IVPNClient/Managers/ConnectionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,6 @@ class ConnectionManager {
self.updateOpenVPNLogFile()
self.updateWireGuardLogFile()
self.reconnectAutomatically = false
if self.actionType == .connect {
self.evaluateCloseApp()
}
}
DispatchQueue.delay(2.5) {
if UserDefaults.shared.isV2ray && !V2RayCore.shared.reconnectWithV2ray {
Expand Down Expand Up @@ -128,10 +125,6 @@ class ConnectionManager {
}
}
}

if status == .disconnected && self.actionType == .disconnect {
self.evaluateCloseApp()
}

completion(status)
}
Expand Down Expand Up @@ -575,12 +568,10 @@ class ConnectionManager {
}
}

private func evaluateCloseApp() {
func evaluateCloseApp() {
if closeApp {
closeApp = false
DispatchQueue.delay(1.5) {
UIControl().sendAction(#selector(NSXPCConnection.suspend), to: UIApplication.shared, for: nil)
}
UIControl().sendAction(#selector(NSXPCConnection.suspend), to: UIApplication.shared, for: nil)
}
}

Expand Down
8 changes: 6 additions & 2 deletions IVPNClient/Managers/PurchaseManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ class PurchaseManager: NSObject {
log(.info, message: "[Store] Completing successful in-app purchase \(productId)")
self.complete(transaction)
break
case .success(.unverified(_, _)):
case .success(.unverified(_, let result)):
// Successful purchase but transaction/receipt can't be verified
// Could be a jailbroken phone
log(.info, message: "[Store] Purchase \(productId): success, unverified")
delegate?.purchaseError(error: ErrorResult(status: 500, message: "Purchase is unverified."))
delegate?.purchaseError(error: ErrorResult(status: 500, message: "Purchase is unverified: \(result.localizedDescription)."))
break
case .pending:
// Transaction waiting on SCA (Strong Customer Authentication) or
Expand All @@ -117,6 +117,10 @@ class PurchaseManager: NSObject {
continue
}

guard ProductId.all.contains(transaction.productID) else {
continue
}

if transaction.revocationDate == nil {
log(.info, message: "[Store] Completing unfinished purchase \(transaction.productID)")
complete(transaction)
Expand Down
97 changes: 97 additions & 0 deletions IVPNClient/Models/AppIntents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//
// AddNoteIntent.swift
// IVPN iOS app
// https://github.com/ivpn/ios-app
//
// Created by Juraj Hilje on 2024-09-04.
// Copyright (c) 2024 IVPN Limited.
//
// This file is part of the IVPN iOS app.
//
// The IVPN iOS app is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any later version.
//
// The IVPN iOS app is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License
// along with the IVPN iOS app. If not, see <https://www.gnu.org/licenses/>.
//


import AppIntents

@available(iOS 16, *)
struct Connect: AppIntent {
static var title = LocalizedStringResource("Connect VPN")
static var description = IntentDescription("Connect to the VPN")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: Connect")
NotificationCenter.default.post(name: Notification.Name.IntentConnect, object: nil)
return .result()
}
}

@available(iOS 16, *)
struct Disconnect: AppIntent {
static var title = LocalizedStringResource("Disconnect VPN")
static var description = IntentDescription("Disconnect from the VPN")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: Disconnect")
NotificationCenter.default.post(name: Notification.Name.IntentDisconnect, object: nil)
return .result()
}
}

@available(iOS 16, *)
struct AntiTrackerEnable: AppIntent {
static var title = LocalizedStringResource("Enable AntiTracker")
static var description = IntentDescription("Enables the AntiTracker")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: EnableAntiTracker")
NotificationCenter.default.post(name: Notification.Name.IntentAntiTrackerEnable, object: nil)
return .result()
}
}

@available(iOS 16, *)
struct AntiTrackerDisable: AppIntent {
static var title = LocalizedStringResource("Disable AntiTracker")
static var description = IntentDescription("Disables the AntiTracker")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: DisableAntiTracker")
NotificationCenter.default.post(name: Notification.Name.IntentAntiTrackerDisable, object: nil)
return .result()
}
}

@available(iOS 16, *)
struct CustomDNSEnable: AppIntent {
static var title = LocalizedStringResource("Enable Custom DNS")
static var description = IntentDescription("Enables the Custom DNS")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: EnableCustomDNS")
NotificationCenter.default.post(name: Notification.Name.IntentCustomDNSEnable, object: nil)
return .result()
}
}

@available(iOS 16, *)
struct CustomDNSDisable: AppIntent {
static var title = LocalizedStringResource("Disable Custom DNS")
static var description = IntentDescription("Disables the Custom DNS")

func perform() async throws -> some IntentResult {
log(.info, message: "App Intent handler: DisableCustomDNS")
NotificationCenter.default.post(name: Notification.Name.IntentCustomDNSDisable, object: nil)
return .result()
}
}
34 changes: 34 additions & 0 deletions IVPNClient/PrivacyInfo.xcprivacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
<string>1C8F.1</string>
</array>
</dict>
</array>
</dict>
</plist>
11 changes: 11 additions & 0 deletions IVPNClient/Scenes/AccountScreen/AccountViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,18 @@ class AccountViewController: UITableViewController {
}
}

@IBAction func toggleAccountHidden(_ sender: Any) {
let hidden = accountView.isAccountHidden
accountView.toggleAccountVisibility(hide: !hidden)
accountView.isAccountHidden = !hidden
}

// MARK: - View Lifecycle -

override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = UIColor.init(named: Theme.ivpnBackgroundQuaternary)
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
initNavigationBar()
addObservers()
accountView.setupView(viewModel: viewModel)
Expand All @@ -93,6 +100,10 @@ class AccountViewController: UITableViewController {
sessionManager.getSessionStatus()
}

@objc func willEnterForeground(notification: NSNotification) {
accountView.setupView(viewModel: viewModel)
}

// MARK: - Observers -

func addObservers() {
Expand Down
22 changes: 22 additions & 0 deletions IVPNClient/Scenes/AccountScreen/View/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ class AccountView: UITableView {
@IBOutlet weak var deviceNameTitle: UILabel!
@IBOutlet weak var deviceName: UILabel!
@IBOutlet weak var header: UIView!
@IBOutlet weak var hideAccountButton: UIButton!

// MARK: - Properties -

var isAccountHidden = true
private var serviceType = ServiceType.getType(currentPlan: Application.shared.serviceStatus.currentPlan)

// MARK: - Methods -
Expand All @@ -58,10 +60,30 @@ class AccountView: UITableView {
header.frame = CGRect(x: 0, y: 0, width: Int(header.frame.width), height: headerHeight)
reloadData()
layoutIfNeeded()
toggleAccountVisibility(hide: isAccountHidden)
}

func initQRCode(viewModel: AccountViewModel) {
qrCodeImage.image = UIImage.generateQRCode(from: viewModel.accountId)
toggleAccountVisibility(hide: isAccountHidden)
}

func toggleAccountVisibility(hide: Bool) {
if hide {
hideAccountButton.setImage(UIImage.init(systemName: "eye.slash.fill"), for: .normal)
accountIdLabel.removeBlur()
accountIdLabel.addBlur(2)
accountIdLabel.alpha = 0.7
qrCodeImage.removeBlur()
qrCodeImage.addBlur(2)
qrCodeImage.alpha = 0.5
} else {
hideAccountButton.setImage(UIImage.init(systemName: "eye.fill"), for: .normal)
accountIdLabel.removeBlur()
accountIdLabel.alpha = 1
qrCodeImage.removeBlur()
qrCodeImage.alpha = 1
}
}

}
Loading

0 comments on commit 2e1b8e8

Please sign in to comment.