diff --git a/native/ios/MatchLiveTv.xcodeproj/project.pbxproj b/native/ios/MatchLiveTv.xcodeproj/project.pbxproj
index 35face1..81d39ea 100644
--- a/native/ios/MatchLiveTv.xcodeproj/project.pbxproj
+++ b/native/ios/MatchLiveTv.xcodeproj/project.pbxproj
@@ -677,11 +677,11 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_API_BASE_URL = "$(API_BASE_URL)";
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv.tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -698,7 +698,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = S8Q9TWBRG5;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MatchLiveTv/Resources/Info.plist;
@@ -709,7 +709,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -727,7 +727,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = S8Q9TWBRG5;
ENABLE_TESTABILITY = YES;
GENERATE_INFOPLIST_FILE = NO;
@@ -739,7 +739,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -756,11 +756,11 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_API_BASE_URL = "$(API_BASE_URL)";
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv.tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -775,11 +775,11 @@
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_API_BASE_URL = "$(API_BASE_URL)";
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv.uitests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -794,11 +794,11 @@
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_API_BASE_URL = "$(API_BASE_URL)";
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = "com.matchlivetv.match-live-tv.uitests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
diff --git a/native/ios/MatchLiveTv/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/native/ios/MatchLiveTv/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png
index a625fdf..7aead6f 100644
Binary files a/native/ios/MatchLiveTv/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png and b/native/ios/MatchLiveTv/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png differ
diff --git a/native/ios/MatchLiveTv/Resources/Info.plist b/native/ios/MatchLiveTv/Resources/Info.plist
index 04a65da..e474af1 100644
--- a/native/ios/MatchLiveTv/Resources/Info.plist
+++ b/native/ios/MatchLiveTv/Resources/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 2.0.12
+ 2.0.13
CFBundleURLTypes
@@ -32,7 +32,7 @@
CFBundleVersion
- 35
+ 37
LSRequiresIPhoneOS
NSCameraUsageDescription
diff --git a/native/ios/MatchLiveTvTests/ApiInstantTests.swift b/native/ios/MatchLiveTvTests/ApiInstantTests.swift
index f6d7c3b..119e302 100644
--- a/native/ios/MatchLiveTvTests/ApiInstantTests.swift
+++ b/native/ios/MatchLiveTvTests/ApiInstantTests.swift
@@ -1,7 +1,45 @@
import XCTest
+@testable import MatchLiveTv
-/// Smoke test eseguibile senza @testable import (validazione logica pura).
+/// Logica calendario/API reale + smoke parser (RTMP/overlay) senza dipendenze di rete.
final class ApiInstantTests: XCTestCase {
+ func testParseApiInstantRailsOffsetWithMillis() {
+ let now = iso("2026-06-06T16:00:00Z")
+ XCTAssertTrue(ApiInstant.isScheduledOnCalendar("2026-06-06T20:00:00.000+02:00", now: now))
+ }
+
+ func testIsScheduledOnCalendarKeepsThisMorningAfterKickoff() {
+ let kickoff = "2026-09-06T07:00:00Z"
+ let now = iso("2026-09-06T08:30:00Z")
+ XCTAssertTrue(ApiInstant.isScheduledOnCalendar(kickoff, now: now))
+ XCTAssertFalse(ApiInstant.isScheduledFuture(kickoff, now: now))
+ }
+
+ func testIsScheduledOnCalendarHidesYesterday() {
+ let yesterday = "2026-09-05T10:00:00Z"
+ let now = iso("2026-09-06T08:30:00Z")
+ XCTAssertFalse(ApiInstant.isScheduledOnCalendar(yesterday, now: now))
+ }
+
+ func testConcurrentStreamForbiddenShowsApiMessage() {
+ let body = """
+ {"error":"Hai già una diretta in corso con questo account. Chiudila prima di avviarne un’altra.","error_code":"user_concurrent_stream"}
+ """
+ XCTAssertEqual(
+ APIError.http(403, body).errorDescription,
+ "Hai già una diretta in corso con questo account. Chiudila prima di avviarne un’altra."
+ )
+ }
+
+ private func iso(_ value: String) -> Date {
+ let f = ISO8601DateFormatter()
+ f.formatOptions = [.withInternetDateTime]
+ guard let date = f.date(from: value) else {
+ XCTFail("Data ISO non valida: \(value)")
+ return Date()
+ }
+ return date
+ }
func testOverlayKindMapping() {
XCTAssertEqual(OverlayKindMapping.fromApi("basket"), "basket")
XCTAssertEqual(OverlayKindMapping.fromApi("none"), "none")
diff --git a/native/ios/MatchLiveTvTests/MatchScoringRulesTests.swift b/native/ios/MatchLiveTvTests/MatchScoringRulesTests.swift
index 498001b..1f19614 100644
--- a/native/ios/MatchLiveTvTests/MatchScoringRulesTests.swift
+++ b/native/ios/MatchLiveTvTests/MatchScoringRulesTests.swift
@@ -121,6 +121,65 @@ final class MatchScoringRulesTests: XCTestCase {
}
}
+final class MatchHubCalendarTests: XCTestCase {
+ func testHubKeepsMatchAfterKickoffSameDay() {
+ let now = iso("2026-09-06T08:30:00Z")
+ let match = hubMatch(scheduledAt: "2026-09-06T07:00:00Z")
+ XCTAssertTrue(MatchHubFilter.coachHubVisible(match, now: now))
+ XCTAssertTrue(MatchHubFilter.isOnCalendar(match, now: now))
+ XCTAssertFalse(MatchHubFilter.isScheduledFuture(match, now: now))
+ }
+
+ func testHubHidesYesterdayMatch() {
+ let now = iso("2026-09-06T08:30:00Z")
+ let match = hubMatch(scheduledAt: "2026-09-05T10:00:00Z")
+ XCTAssertFalse(MatchHubFilter.coachHubVisible(match, now: now))
+ }
+
+ func testHubRespectsApiCoachHubVisibleFalse() {
+ let now = iso("2026-09-06T08:30:00Z")
+ let match = hubMatch(scheduledAt: "2026-09-06T07:00:00Z", coachHubVisible: false)
+ XCTAssertFalse(MatchHubFilter.coachHubVisible(match, now: now))
+ }
+
+ private func iso(_ value: String) -> Date {
+ let f = ISO8601DateFormatter()
+ f.formatOptions = [.withInternetDateTime]
+ return f.date(from: value)!
+ }
+
+ private func hubMatch(scheduledAt: String?, coachHubVisible: Bool? = nil) -> Match {
+ Match(
+ id: "match-hub",
+ teamId: "team-1",
+ teamName: "Team MLTV",
+ opponentName: "Team Guest",
+ location: nil,
+ scheduledAt: scheduledAt,
+ sportKey: "pallavolo",
+ sportLabel: "Pallavolo",
+ boardType: "volley",
+ overlayKind: "volley",
+ effectiveOverlayKind: "volley",
+ setsToWin: 3,
+ category: nil,
+ scoringRules: nil,
+ activeSessionId: nil,
+ activeSessionStatus: nil,
+ streamCompleted: false,
+ coachHubVisible: coachHubVisible,
+ homePrimaryColor: "#FF2D2D",
+ homeSecondaryColor: nil,
+ homeLogoUrl: nil,
+ opponentPrimaryColor: "#1E3A8A",
+ opponentLogoUrl: nil,
+ effectiveCoverUrl: nil,
+ coverSource: "default",
+ customCoverEnabled: false
+ )
+ }
+}
+
final class ScoreboardOverlayTests: XCTestCase {
func testScoreboardColumnsAfterSetClosed() {
let score = ScoreState(
diff --git a/native/ios/generate_xcodeproj.py b/native/ios/generate_xcodeproj.py
index b8ef250..0d754af 100755
--- a/native/ios/generate_xcodeproj.py
+++ b/native/ios/generate_xcodeproj.py
@@ -96,7 +96,7 @@ lines += [
app_settings = """
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = MatchLiveTv/Resources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
@@ -107,7 +107,7 @@ app_settings = """
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = com.matchlivetv.match-live-tv;
PRODUCT_NAME = "$(TARGET_NAME)";
API_BASE_URL = "https://www.matchlivetv.it";
@@ -122,10 +122,10 @@ app_debug_settings = app_settings + """
test_settings = """
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 35;
+ CURRENT_PROJECT_VERSION = 37;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
- MARKETING_VERSION = 2.0.12;
+ MARKETING_VERSION = 2.0.13;
PRODUCT_BUNDLE_IDENTIFIER = com.matchlivetv.match-live-tv.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;