From d5077513291e148fedd197301e3f5e0423fa0b47 Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Thu, 8 Feb 2024 19:54:40 +0000
Subject: [PATCH 1/6] iOS: Re-enable activities and update URLs

---
 apps/ios/GuideDogs.xcodeproj/project.pbxproj  | 19 ++++++++-----------
 .../GuideDogs/Assets/PropertyLists/Info.plist |  2 +-
 .../AuthoredActivityMetadata.swift            | 14 ++++++--------
 .../Main Menu/MenuViewController.swift        |  6 +++++-
 .../Home/HomeViewController.swift             |  1 +
 5 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/apps/ios/GuideDogs.xcodeproj/project.pbxproj b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
index 182f0ea8..bddb29a6 100644
--- a/apps/ios/GuideDogs.xcodeproj/project.pbxproj
+++ b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
@@ -6472,7 +6472,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/SoundscapeDF.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 7;
+				CURRENT_PROJECT_VERSION = 15;
 				DEVELOPMENT_TEAM = "";
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
@@ -6503,7 +6503,7 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/GuideDogs",
 				);
-				MARKETING_VERSION = 1.0.0;
+				MARKETING_VERSION = 1.0.2;
 				OTHER_LDFLAGS = "$(inherited)";
 				OTHER_SWIFT_FLAGS = "$(inherited) -DADHOC";
 				PRODUCT_BUNDLE_IDENTIFIER = "services.soundscape-adhoc";
@@ -6758,7 +6758,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 7;
+				CURRENT_PROJECT_VERSION = 15;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
@@ -6790,7 +6790,7 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/GuideDogs",
 				);
-				MARKETING_VERSION = 1.0.0;
+				MARKETING_VERSION = 1.0.2;
 				OTHER_LDFLAGS = "$(inherited)";
 				OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG";
 				PRODUCT_BUNDLE_IDENTIFIER = "services.soundscape-debug";
@@ -6817,11 +6817,9 @@
 				CLANG_STATIC_ANALYZER_MODE = deep;
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
-				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 7;
-				DEVELOPMENT_TEAM = "";
-				"DEVELOPMENT_TEAM[sdk=iphoneos*]" = X4H33NKGKY;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 15;
+				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
 				FILE_SHARING_ENABLED = NO;
@@ -6850,13 +6848,12 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/GuideDogs",
 				);
-				MARKETING_VERSION = 1.0.0;
+				MARKETING_VERSION = 1.0.2;
 				OTHER_LDFLAGS = "$(inherited)";
 				OTHER_SWIFT_FLAGS = "$(inherited) -DRELEASE";
 				PRODUCT_BUNDLE_IDENTIFIER = services.soundscape;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
-				"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore services.soundscape 1691940011";
 				RUN_CLANG_STATIC_ANALYZER = YES;
 				SDKROOT = iphoneos;
 				SWIFT_OBJC_BRIDGING_HEADER = "GuideDogs/Code/App/Soundscape-Bridging-Header.h";
diff --git a/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist b/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
index 51e2cc03..d6b176e9 100644
--- a/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
+++ b/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
@@ -80,7 +80,7 @@
 	<key>CFBundleSpokenName</key>
 	<string>${BUNDLE_SPOKEN_NAME}</string>
 	<key>CFBundleVersion</key>
-	<string>7</string>
+	<string>${CURRENT_PROJECT_VERSION}</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<false/>
 	<key>LSApplicationCategoryType</key>
diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift
index bea9aae3..e7650de1 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift	
@@ -45,20 +45,18 @@ struct AuthoredActivityMetadata: Codable, CustomStringConvertible {
     }
     
     /// Builds the remote server path that the content can be downloaded from
-    /// E.G. https://share.openscape.io/experiences/some-activity.gpx
+    /// E.G. https://share.soundscape.services/experiences/<id>/activity.gpx
     var downloadPath: URL? {
-        var components = URLComponents()
+    var components = URLComponents()
         
         switch linkVersion {
-        case .v1:
-            components.scheme = "https"
-            components.host = "share.openscape.io"
-            components.path = "/experiences/\(id).gpx"
         case .v2, .v3:
-            // Version 2 and 3 links also look the same (perk of forking)
             components.scheme = "https"
             components.host = "share.soundscape.services"
-            components.path = "experiences/\(id).gpx"
+            components.path = "activities/\(id)/activity.gpx"
+        default:
+            // no other versions currently supported
+            break
         }
         
         return components.url
diff --git a/apps/ios/GuideDogs/Code/Visual UI/Controls/Main Menu/MenuViewController.swift b/apps/ios/GuideDogs/Code/Visual UI/Controls/Main Menu/MenuViewController.swift
index 368b6585..436512e5 100644
--- a/apps/ios/GuideDogs/Code/Visual UI/Controls/Main Menu/MenuViewController.swift	
+++ b/apps/ios/GuideDogs/Code/Visual UI/Controls/Main Menu/MenuViewController.swift	
@@ -10,11 +10,12 @@ import UIKit
 import SafariServices
 
 enum MenuItem {
-    case home, devices, help, settings, status, feedback, rate, share
+    case home, recreation, devices, help, settings, status, feedback, rate, share
     
     var localizedString: String {
         switch self {
         case .home:       return GDLocalizedString("ui.menu.close")
+        case .recreation: return GDLocalizedString("menu.events")
         case .devices:    return GDLocalizedString("menu.devices")
         case .help:       return GDLocalizedString("menu.help_and_tutorials")
         case .settings:   return GDLocalizedString("settings.screen_title")
@@ -28,6 +29,7 @@ enum MenuItem {
     var accessibilityString: String {
         switch self {
         case .home:       return GDLocalizedString("ui.menu.close")
+        case .recreation: return GDLocalizedString("menu.events")
         case .devices:    return GDLocalizedString("menu.devices")
         case .help:       return GDLocalizedString("menu.help_and_tutorials")
         case .settings:   return GDLocalizedString("settings.screen_title")
@@ -41,6 +43,7 @@ enum MenuItem {
     var icon: UIImage? {
         switch self {
         case .home:       return UIImage(named: "ic_chevron_left_28px")
+        case .recreation: return UIImage(named: "nordic_walking_white_28dp")
         case .devices:    return UIImage(named: "baseline-headset-28px")
         case .help:       return UIImage(named: "ic_help_outline_28px")
         case .settings:   return UIImage(named: "ic_settings_28px")
@@ -61,6 +64,7 @@ class MenuViewController: UIViewController {
     override func loadView() {
         // Build views for menu items
         menuView.addMenuItem(.devices)
+        menuView.addMenuItem(.recreation)
         menuView.addMenuItem(.settings)
         menuView.addMenuItem(.help)
         menuView.addMenuItem(.feedback)
diff --git a/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Home/HomeViewController.swift b/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Home/HomeViewController.swift
index ef9a222d..a750177b 100644
--- a/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Home/HomeViewController.swift	
+++ b/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Home/HomeViewController.swift	
@@ -38,6 +38,7 @@ class HomeViewController: UIViewController {
         /// - Returns: The segue associated with this menu item
         static func segue(for menuItem: MenuItem) -> String? {
             switch menuItem {
+            case .recreation: return Segue.showRecreationActivities
             case .devices:    return Segue.showManageDevices
             case .help:       return Segue.showHelp
             case .settings:   return Segue.showSettings

From a674da519206ea405a58e43bc3ff5b5d36a17826 Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Fri, 9 Feb 2024 18:32:00 +0000
Subject: [PATCH 2/6] Fix the URL for downloading activities

---
 apps/ios/GuideDogs.xcodeproj/project.pbxproj                | 6 +++---
 .../Components/UniversalLinkComponents.swift                | 2 +-
 .../Data/Authored Activities/AuthoredActivityMetadata.swift | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/apps/ios/GuideDogs.xcodeproj/project.pbxproj b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
index bddb29a6..15ef07b3 100644
--- a/apps/ios/GuideDogs.xcodeproj/project.pbxproj
+++ b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
@@ -6472,7 +6472,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/SoundscapeDF.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 15;
+				CURRENT_PROJECT_VERSION = 16;
 				DEVELOPMENT_TEAM = "";
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
@@ -6758,7 +6758,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 15;
+				CURRENT_PROJECT_VERSION = 16;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
@@ -6818,7 +6818,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 15;
+				CURRENT_PROJECT_VERSION = 16;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
diff --git a/apps/ios/GuideDogs/Code/App/App Delegate/User Activities/Universal Links/Components/UniversalLinkComponents.swift b/apps/ios/GuideDogs/Code/App/App Delegate/User Activities/Universal Links/Components/UniversalLinkComponents.swift
index ae9d79e7..297af555 100644
--- a/apps/ios/GuideDogs/Code/App/App Delegate/User Activities/Universal Links/Components/UniversalLinkComponents.swift	
+++ b/apps/ios/GuideDogs/Code/App/App Delegate/User Activities/Universal Links/Components/UniversalLinkComponents.swift	
@@ -31,7 +31,7 @@ struct UniversalLinkComponents {
          // Add query items
         components.queryItems = queryItems
 
-         return components.url
+        return components.url
     }
     
     // MARK: Initialization
diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift
index e7650de1..3d23c4b4 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityMetadata.swift	
@@ -53,7 +53,7 @@ struct AuthoredActivityMetadata: Codable, CustomStringConvertible {
         case .v2, .v3:
             components.scheme = "https"
             components.host = "share.soundscape.services"
-            components.path = "activities/\(id)/activity.gpx"
+            components.path = "/activities/\(id)/activity.gpx"
         default:
             // no other versions currently supported
             break

From a6129791e87bf6b151b74986d7f711440b44e827 Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Wed, 28 Feb 2024 14:46:28 +0000
Subject: [PATCH 3/6] Support relative links for images and audio files in
 activities.

---
 .../AuthoredActivityContent.swift                  | 14 +++++++-------
 .../AuthoredActivityLoader.swift                   | 13 ++++++++++---
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift
index d829cb01..e0b25c3d 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
@@ -179,7 +179,7 @@ extension AuthoredActivityContent {
     ///
     /// - Parameter gpx: A parsed GPX file
     /// - Returns: An ``AuthoredActivityContent``, or `nil` if parsing failed or required properties were missing. Currently, waypoints or POIs may be skipped if they lack coordinate data.
-    static func parse(gpx: GPXRoot) -> AuthoredActivityContent? {
+    static func parse(gpx: GPXRoot, baseURL: URL) -> AuthoredActivityContent? {
         guard let metadata = gpx.metadata else {
             return nil
         }
@@ -206,7 +206,7 @@ extension AuthoredActivityContent {
         
         var imageURL: URL?
         if let image = metadata.links.first, image.mimetype?.hasPrefix("image") != nil, let href = image.href {
-            imageURL = URL(string: href)
+            imageURL = URL(string: href, relativeTo: baseURL)
         }
         
         // Parse the waypoints and POIs based on the file version
@@ -214,7 +214,7 @@ extension AuthoredActivityContent {
         case "1":
             // Version 1 just uses all the top-level waypoints `<wpt></wpt>` defined in the GPX, in order
             
-            let wpts: [ActivityWaypoint] = waypoints(from: gpx.waypoints)
+            let wpts: [ActivityWaypoint] = waypoints(from: gpx.waypoints, baseURL: baseURL)
             
             // For waypoints in this experience, require names, descriptions, and street addresses
             guard !wpts.isEmpty, !wpts.contains(where: { $0.name == nil }) else {
@@ -242,7 +242,7 @@ extension AuthoredActivityContent {
             }
             
             // Waypoints are strict about requiring names and locations
-            let wpts: [ActivityWaypoint] = waypoints(from: route.points)
+            let wpts: [ActivityWaypoint] = waypoints(from: route.points, baseURL: baseURL)
             
             // For waypoints in this experience, require names, descriptions, and street addresses
             guard !wpts.isEmpty, !wpts.contains(where: { $0.name == nil }) else {
@@ -279,7 +279,7 @@ extension AuthoredActivityContent {
     ///
     /// - Parameter waypoints: an array of ``GPXWaypoint``s
     /// - Returns: an array of ``ActivityWaypoint``s including annotation data (if applicable)
-    private static func waypoints(from waypoints: [GPXWaypoint]) -> [ActivityWaypoint] {
+    private static func waypoints(from waypoints: [GPXWaypoint], baseURL: URL) -> [ActivityWaypoint] {
         let imageMimeTypes = Set(["image/jpeg", "image/jpg", "image/png"])
         let audioMimeTypes = Set(["audio/mpeg", "audio/x-m4a"])
         
@@ -291,7 +291,7 @@ extension AuthoredActivityContent {
             
             let parsedImages: [ActivityWaypointImage] = links.compactMap { link in
                 guard let href = link.href,
-                      let url = URL(string: href) else {
+                      let url = URL(string: href, relativeTo: baseURL) else {
                     return nil
                 }
                 
@@ -300,7 +300,7 @@ extension AuthoredActivityContent {
             
             let parsedAudioClips: [ActivityWaypointAudioClip] = links.compactMap { link in
                 guard let href = link.href,
-                      let url = URL(string: href) else {
+                      let url = URL(string: href, relativeTo: baseURL) else {
                     return nil
                 }
                 
diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityLoader.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityLoader.swift
index b527325a..8b87bd40 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityLoader.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityLoader.swift	
@@ -127,8 +127,15 @@ class AuthoredActivityLoader {
             return nil
         }
         
+        guard let index = knownActivities.events.firstIndex(where: { activityID == $0.id }),
+        let baseURL = knownActivities.events[index].downloadPath else {
+            GDLogAppError("Unable to find download path for activity with ID: \(activityID)")
+            return nil
+        }
+        
+        
         // Parse the GPX file and validate its contents
-        return AuthoredActivityContent.parse(gpx: gpx)
+        return AuthoredActivityContent.parse(gpx: gpx, baseURL: baseURL)
     }
     
     func add(_ activityID: String, linkVersion: UniversalLinkVersion) async throws {
@@ -296,8 +303,8 @@ class AuthoredActivityLoader {
             throw ActivityLoaderError.unableToLoadContent
         }
         
-        guard let content = AuthoredActivityContent.parse(gpx: gpx) else {
-            GDLogWarn(.routeGuidance, "Unable to parse activity content from GPX for \(id)")
+        guard let content = AuthoredActivityContent.parse(gpx: gpx, baseURL: metadata.downloadPath!) else {
+            GDLogWarn(.routeGuidance, "Unable to parse activity content from GPX for \(id), URL = \(metadata.downloadPath)")
             
             NotificationCenter.default.post(name: .didTryActivityUpdate, object: self, userInfo: [
                 Keys.updateSuccess: false,

From 26ea00c265e941989e97cc686ff19684feacfc7d Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Wed, 28 Feb 2024 15:07:30 +0000
Subject: [PATCH 4/6] bump build to 17

---
 apps/ios/GuideDogs.xcodeproj/project.pbxproj | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/apps/ios/GuideDogs.xcodeproj/project.pbxproj b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
index 15ef07b3..4a859822 100644
--- a/apps/ios/GuideDogs.xcodeproj/project.pbxproj
+++ b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
@@ -6472,7 +6472,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/SoundscapeDF.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 16;
+				CURRENT_PROJECT_VERSION = 17;
 				DEVELOPMENT_TEAM = "";
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
@@ -6758,7 +6758,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 16;
+				CURRENT_PROJECT_VERSION = 17;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
@@ -6818,7 +6818,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 16;
+				CURRENT_PROJECT_VERSION = 17;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;

From 1ad37e818650ac29af78774084274040425b6a0d Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Thu, 29 Feb 2024 20:07:55 +0000
Subject: [PATCH 5/6] Fix audio clips in activities, remove landscape from
 orientations. (build 18)

---
 apps/ios/GuideDogs.xcodeproj/project.pbxproj                | 6 +++---
 apps/ios/GuideDogs/Assets/PropertyLists/Info.plist          | 1 -
 .../Data/Authored Activities/AuthoredActivityContent.swift  | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/apps/ios/GuideDogs.xcodeproj/project.pbxproj b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
index 4a859822..e6d8ad19 100644
--- a/apps/ios/GuideDogs.xcodeproj/project.pbxproj
+++ b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
@@ -6472,7 +6472,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/SoundscapeDF.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 17;
+				CURRENT_PROJECT_VERSION = 18;
 				DEVELOPMENT_TEAM = "";
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
@@ -6758,7 +6758,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 17;
+				CURRENT_PROJECT_VERSION = 18;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
@@ -6818,7 +6818,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 17;
+				CURRENT_PROJECT_VERSION = 18;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
diff --git a/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist b/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
index d6b176e9..8541e686 100644
--- a/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
+++ b/apps/ios/GuideDogs/Assets/PropertyLists/Info.plist
@@ -156,7 +156,6 @@
 	<string>UIStatusBarStyleLightContent</string>
 	<key>UISupportedInterfaceOrientations</key>
 	<array>
-		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationPortrait</string>
 	</array>
 	<key>UISupportedInterfaceOrientations~ipad</key>
diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift
index e0b25c3d..7b10ddec 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
@@ -286,7 +286,7 @@ extension AuthoredActivityContent {
         return waypoints.compactMap { wpt in
             let links: [GPXLink] = wpt.extensions?.soundscapeLinkExtensions?.links.filter({
                 guard let mimetype = $0.mimetype else { return false }
-                return imageMimeTypes.contains(mimetype)
+                return imageMimeTypes.contains(mimetype) || audioMimeTypes.contains(mimetype)
             }) ?? []
             
             let parsedImages: [ActivityWaypointImage] = links.compactMap { link in

From 6432b204f5d7e6eefb027d2f36d1d2a81be7f563 Mon Sep 17 00:00:00 2001
From: Robbie Murray <rob@mur.org.uk>
Date: Mon, 4 Mar 2024 17:27:13 +0000
Subject: [PATCH 6/6] Fix audio and image loading for activities, update
 UnitTests bumped build number to 19

---
 apps/ios/GuideDogs.xcodeproj/project.pbxproj        |  9 ++++++---
 .../AuthoredActivityContent.swift                   | 13 +++++++++----
 .../AuthoredActivityContentTest.swift               |  5 +++--
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/apps/ios/GuideDogs.xcodeproj/project.pbxproj b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
index e6d8ad19..b0a8eb6a 100644
--- a/apps/ios/GuideDogs.xcodeproj/project.pbxproj
+++ b/apps/ios/GuideDogs.xcodeproj/project.pbxproj
@@ -6472,7 +6472,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/SoundscapeDF.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Manual;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 19;
 				DEVELOPMENT_TEAM = "";
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
@@ -6533,6 +6533,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEVELOPMENT_TEAM = X4H33NKGKY;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				GENERATE_INFOPLIST_FILE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@@ -6567,6 +6568,7 @@
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = X4H33NKGKY;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				GENERATE_INFOPLIST_FILE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@@ -6598,6 +6600,7 @@
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = X4H33NKGKY;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				GENERATE_INFOPLIST_FILE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@@ -6758,7 +6761,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 19;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
@@ -6818,7 +6821,7 @@
 				CODE_SIGN_ENTITLEMENTS = GuideDogs/Assets/PropertyLists/Soundscape.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 19;
 				DEVELOPMENT_TEAM = X4H33NKGKY;
 				EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = YES;
 				ENABLE_BITCODE = NO;
diff --git a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift
index 7b10ddec..f7a30407 100644
--- a/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
+++ b/apps/ios/GuideDogs/Code/Data/Authored Activities/AuthoredActivityContent.swift	
@@ -284,12 +284,12 @@ extension AuthoredActivityContent {
         let audioMimeTypes = Set(["audio/mpeg", "audio/x-m4a"])
         
         return waypoints.compactMap { wpt in
-            let links: [GPXLink] = wpt.extensions?.soundscapeLinkExtensions?.links.filter({
+            let imageLinks: [GPXLink] = wpt.extensions?.soundscapeLinkExtensions?.links.filter({
                 guard let mimetype = $0.mimetype else { return false }
-                return imageMimeTypes.contains(mimetype) || audioMimeTypes.contains(mimetype)
+                return imageMimeTypes.contains(mimetype)
             }) ?? []
             
-            let parsedImages: [ActivityWaypointImage] = links.compactMap { link in
+            let parsedImages: [ActivityWaypointImage] = imageLinks.compactMap { link in
                 guard let href = link.href,
                       let url = URL(string: href, relativeTo: baseURL) else {
                     return nil
@@ -298,7 +298,12 @@ extension AuthoredActivityContent {
                 return ActivityWaypointImage(url: url, altText: link.text)
             }
             
-            let parsedAudioClips: [ActivityWaypointAudioClip] = links.compactMap { link in
+            let audioLinks: [GPXLink] = wpt.extensions?.soundscapeLinkExtensions?.links.filter({
+                guard let mimetype = $0.mimetype else { return false }
+                return audioMimeTypes.contains(mimetype)
+            }) ?? []
+            
+            let parsedAudioClips: [ActivityWaypointAudioClip] = audioLinks.compactMap { link in
                 guard let href = link.href,
                       let url = URL(string: href, relativeTo: baseURL) else {
                     return nil
diff --git a/apps/ios/UnitTests/Data/Authored Activities/AuthoredActivityContentTest.swift b/apps/ios/UnitTests/Data/Authored Activities/AuthoredActivityContentTest.swift
index d5afbb3b..eef29fd4 100644
--- a/apps/ios/UnitTests/Data/Authored Activities/AuthoredActivityContentTest.swift	
+++ b/apps/ios/UnitTests/Data/Authored Activities/AuthoredActivityContentTest.swift	
@@ -15,6 +15,7 @@ final class AuthoredActivityContentTest: XCTestCase {
     
     // MARK: Test GPX Parsing
     
+    static let baseURL = URL(string: "https://example.com")!
     /// Tests parsing from GPX
     /// Using `GPXSoundscapeSharedContentExtensions` v1
     /// And minimal other details
@@ -63,7 +64,7 @@ final class AuthoredActivityContentTest: XCTestCase {
             XCTFail("Failed to get parsedData")
             return
         }
-        guard let activity = AuthoredActivityContent.parse(gpx: root) else {
+        guard let activity = AuthoredActivityContent.parse(gpx: root, baseURL: AuthoredActivityContentTest.baseURL) else {
             XCTFail("Failed to create AuthoredActivityContent from GPXRoot")
             return
         }
@@ -150,7 +151,7 @@ final class AuthoredActivityContentTest: XCTestCase {
             XCTFail("Failed to get parsedData")
             return
         }
-        guard let activity = AuthoredActivityContent.parse(gpx: root) else {
+        guard let activity = AuthoredActivityContent.parse(gpx: root, baseURL: AuthoredActivityContentTest.baseURL) else {
             XCTFail("Failed to create AuthoredActivityContent from GPXRoot")
             return
         }