Skip to content

Commit 583f512

Browse files
authored
Merge pull request #416 from superwall/develop
4.12.5
2 parents 9f912c8 + defb07a commit 583f512

File tree

10 files changed

+121
-6
lines changed

10 files changed

+121
-6
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
The changelog for `SuperwallKit`. Also see the [releases](https://github.com/superwall/Superwall-iOS/releases) on GitHub.
44

5+
## 4.12.5
6+
7+
### Enhancements
8+
9+
- Adds microphone permission request support.
10+
11+
### Fixes
12+
13+
- Fixes issue where the notification permission prompt would not appear if provisional notification permission was already granted.
14+
515
## 4.12.4
616

717
### Enhancements

CLAUDE.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ SuperwallKit is an iOS SDK for remote paywall configuration and A/B testing. The
6666

6767
### Version Management
6868

69-
- Version is defined in `Sources/SuperwallKit/Misc/Constants.swift` line 21
70-
- Pre-commit hook automatically syncs version to `SuperwallKit.podspec`
69+
When bumping the version, update all three files:
70+
1. `Sources/SuperwallKit/Misc/Constants.swift` (line 21)
71+
2. `SuperwallKit.podspec` (s.version)
72+
3. `CHANGELOG.md` (add new version entry at top)
73+
7174
- Follows semantic versioning
7275

7376
### Testing

Sources/SuperwallKit/Misc/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ let sdkVersion = """
1818
*/
1919

2020
let sdkVersion = """
21-
4.12.4
21+
4.12.5
2222
"""

Sources/SuperwallKit/Permissions/AuthorizationStatus+PermissionStatus.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ extension AVAuthorizationStatus {
5555
}
5656
}
5757

58+
extension AVAudioSession.RecordPermission {
59+
var toPermissionStatus: PermissionStatus {
60+
switch self {
61+
case .granted:
62+
return .granted
63+
case .denied,
64+
.undetermined:
65+
return .denied
66+
@unknown default:
67+
return .unsupported
68+
}
69+
}
70+
}
71+
5872
extension Int {
5973
var toContactsPermissionStatus: PermissionStatus {
6074
// CNAuthorizationStatus:
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// PermissionHandler+Microphone.swift
3+
// SuperwallKit
4+
//
5+
// Created by Yusuf Tör on 13/01/2026.
6+
//
7+
8+
import AVFoundation
9+
10+
extension PermissionHandler {
11+
func checkMicrophonePermission() -> PermissionStatus {
12+
return AVAudioSession.sharedInstance().recordPermission.toPermissionStatus
13+
}
14+
15+
@MainActor
16+
func requestMicrophonePermission() async -> PermissionStatus {
17+
guard hasPlistKey(PlistKey.microphone) else {
18+
await showMissingPlistKeyAlert(
19+
for: PlistKey.microphone,
20+
permissionName: "Microphone"
21+
)
22+
return .unsupported
23+
}
24+
25+
let currentStatus = checkMicrophonePermission()
26+
if currentStatus == .granted {
27+
return .granted
28+
}
29+
30+
return await withCheckedContinuation { continuation in
31+
AVAudioSession.sharedInstance().requestRecordPermission { granted in
32+
continuation.resume(returning: granted ? .granted : .denied)
33+
}
34+
}
35+
}
36+
}

Sources/SuperwallKit/Permissions/PermissionHandler.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ final class PermissionHandler: PermissionHandling {
2121
static let locationWhenInUse = "NSLocationWhenInUseUsageDescription"
2222
static let locationAlways = "NSLocationAlwaysAndWhenInUseUsageDescription"
2323
static let tracking = "NSUserTrackingUsageDescription"
24+
static let microphone = "NSMicrophoneUsageDescription"
2425
}
2526

2627
func hasPlistKey(_ key: String) -> Bool {
@@ -69,6 +70,8 @@ final class PermissionHandler: PermissionHandling {
6970
return checkCameraPermission()
7071
case .tracking:
7172
return checkTrackingPermission()
73+
case .microphone:
74+
return checkMicrophonePermission()
7275
}
7376
}
7477

@@ -88,6 +91,8 @@ final class PermissionHandler: PermissionHandling {
8891
return await requestCameraPermission()
8992
case .tracking:
9093
return await requestTrackingPermission()
94+
case .microphone:
95+
return await requestMicrophonePermission()
9196
}
9297
}
9398
}

Sources/SuperwallKit/Permissions/PermissionType.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ enum PermissionType: String, Decodable {
1717
case contacts
1818
case camera
1919
case tracking
20+
case microphone
2021
}

Sources/SuperwallKit/StoreKit/Transactions/Notifications/NotificationScheduler.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,7 @@ actor NotificationScheduler {
164164
let settings = await notificationCenter.notificationSettings()
165165
switch settings.authorizationStatus {
166166
case .authorized,
167-
.ephemeral,
168-
.provisional:
167+
.ephemeral:
169168
return true
170169
default:
171170
return false

SuperwallKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22

33
s.name = "SuperwallKit"
4-
s.version = "4.12.4"
4+
s.version = "4.12.5"
55
s.summary = "Superwall: In-App Paywalls Made Easy"
66
s.description = "Paywall infrastructure for mobile apps :) we make things like editing your paywall and running price tests as easy as clicking a few buttons. superwall.com"
77

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// MicrophonePermissionTests.swift
3+
// SuperwallKitTests
4+
//
5+
// Created by Yusuf Tör on 13/01/2026.
6+
//
7+
8+
import AVFoundation
9+
import Foundation
10+
import Testing
11+
@testable import SuperwallKit
12+
13+
@Suite
14+
struct MicrophonePermissionConversionTests {
15+
@Test func toPermissionStatus_granted_returnsGranted() {
16+
let status = AVAudioSession.RecordPermission.granted
17+
#expect(status.toPermissionStatus == .granted)
18+
}
19+
20+
@Test func toPermissionStatus_denied_returnsDenied() {
21+
let status = AVAudioSession.RecordPermission.denied
22+
#expect(status.toPermissionStatus == .denied)
23+
}
24+
25+
@Test func toPermissionStatus_undetermined_returnsDenied() {
26+
let status = AVAudioSession.RecordPermission.undetermined
27+
#expect(status.toPermissionStatus == .denied)
28+
}
29+
}
30+
31+
@Suite
32+
struct PermissionTypeMicrophoneTests {
33+
@Test func microphoneCase_exists() {
34+
let permission = PermissionType.microphone
35+
#expect(permission.rawValue == "microphone")
36+
}
37+
38+
@Test func microphone_isDecodable() throws {
39+
let json = """
40+
"microphone"
41+
""".data(using: .utf8)!
42+
43+
let decoder = JSONDecoder()
44+
let result = try decoder.decode(PermissionType.self, from: json)
45+
#expect(result == .microphone)
46+
}
47+
}

0 commit comments

Comments
 (0)