diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 0000000..9d1dc2b --- /dev/null +++ b/.github/workflows/ios.yml @@ -0,0 +1,48 @@ +name: iOS starter workflow + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + name: Build and Test default scheme using any available iPhone simulator + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set Default Scheme + run: | + scheme_list=$(xcodebuild -list -json | tr -d "\n") + default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['schemes'][0]") + echo $default | cat >default + echo Using default scheme: $default + - name: Enable PrepareLicenseList Plugin + run: | + defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES + defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES + - name: Build + env: + scheme: ${{ 'default' }} + platform: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + if [ $scheme = default ]; then scheme=$(cat default); fi + if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi + file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` + xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" + - name: Test + env: + scheme: ${{ 'default' }} + platform: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + if [ $scheme = default ]; then scheme=$(cat default); fi + if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi + file_to_build=`echo $file_to_build | awk '{$1=$1;print}'` + xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh new file mode 100644 index 0000000..a20daa3 --- /dev/null +++ b/ci_scripts/ci_post_clone.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES +defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES diff --git a/harmonie.xcodeproj/project.pbxproj b/harmonie.xcodeproj/project.pbxproj index 5878b9b..d438530 100644 --- a/harmonie.xcodeproj/project.pbxproj +++ b/harmonie.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ @@ -13,6 +13,8 @@ 20102BCF2C157C77007738D3 /* HarmonieApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20102BCE2C157C77007738D3 /* HarmonieApp.swift */; }; 2019D1ED2B9C3AEC0094E358 /* View+frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2019D1EC2B9C3AEC0094E358 /* View+frame.swift */; }; 2019D1F02B9C3CBF0094E358 /* SizeViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2019D1EF2B9C3CBF0094E358 /* SizeViewModifier.swift */; }; + 201B430D2C27F44100879A2F /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201B430C2C27F44100879A2F /* Errors.swift */; }; + 201B43102C27F48E00879A2F /* LicenseList in Frameworks */ = {isa = PBXBuildFile; productRef = 201B430F2C27F48E00879A2F /* LicenseList */; }; 201E40E52B9B55E300321521 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201E40E42B9B55E300321521 /* LoginView.swift */; }; 202844C22BA5CC4F00357D09 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202844C12BA5CC4F00357D09 /* FavoritesView.swift */; }; 202844C42BA5CE1B00357D09 /* LocationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 202844C32BA5CE1B00357D09 /* LocationsView.swift */; }; @@ -37,7 +39,7 @@ 209938A82C109B08003B4A8A /* View+sectioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209938A72C109B08003B4A8A /* View+sectioning.swift */; }; 209938AA2C109C8C003B4A8A /* HASection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209938A92C109C8C003B4A8A /* HASection.swift */; }; 209D77302C2251C200A11666 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209D772F2C2251C200A11666 /* MainTabView.swift */; }; - 20E3FC032C245B5300E336E3 /* HarmonieErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E3FC022C245B5300E336E3 /* HarmonieErrors.swift */; }; + 20B26CB62C283BE2000E1A79 /* VRCKit in Frameworks */ = {isa = PBXBuildFile; productRef = 20B26CB52C283BE2000E1A79 /* VRCKit */; }; 20F213DD2C11FB470061DE29 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 20F213DC2C11FB470061DE29 /* NukeUI */; }; /* End PBXBuildFile section */ @@ -65,6 +67,7 @@ 20102BCE2C157C77007738D3 /* HarmonieApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HarmonieApp.swift; sourceTree = ""; }; 2019D1EC2B9C3AEC0094E358 /* View+frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+frame.swift"; sourceTree = ""; }; 2019D1EF2B9C3CBF0094E358 /* SizeViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeViewModifier.swift; sourceTree = ""; }; + 201B430C2C27F44100879A2F /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 201E40E42B9B55E300321521 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 202844C12BA5CC4F00357D09 /* FavoritesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesView.swift; sourceTree = ""; }; 202844C32BA5CE1B00357D09 /* LocationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsView.swift; sourceTree = ""; }; @@ -89,7 +92,6 @@ 209938A72C109B08003B4A8A /* View+sectioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+sectioning.swift"; sourceTree = ""; }; 209938A92C109C8C003B4A8A /* HASection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HASection.swift; sourceTree = ""; }; 209D772F2C2251C200A11666 /* MainTabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; - 20E3FC022C245B5300E336E3 /* HarmonieErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HarmonieErrors.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -97,8 +99,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 201B43102C27F48E00879A2F /* LicenseList in Frameworks */, 20F213DD2C11FB470061DE29 /* NukeUI in Frameworks */, 20395DC22B945EEF00003921 /* VRCKit in Frameworks */, + 20B26CB62C283BE2000E1A79 /* VRCKit in Frameworks */, 206AC9042BFA087A00E0D5AE /* AsyncSwiftUI in Frameworks */, 206AC9012BFA07AA00E0D5AE /* AsyncSwiftUI in Frameworks */, ); @@ -238,10 +242,10 @@ isa = PBXGroup; children = ( 200FA7AC2C1D61AB0001E655 /* CircleURLImage.swift */, + 201B430C2C27F44100879A2F /* Errors.swift */, 2044ADEF2BC264240057BA6A /* StatusColor.swift */, 205B3BF32C00D8BF0097D62A /* ProgressScreen.swift */, 209938A92C109C8C003B4A8A /* HASection.swift */, - 20E3FC022C245B5300E336E3 /* HarmonieErrors.swift */, ); path = Utils; sourceTree = ""; @@ -267,6 +271,8 @@ 206AC9002BFA07AA00E0D5AE /* AsyncSwiftUI */, 206AC9032BFA087A00E0D5AE /* AsyncSwiftUI */, 20F213DC2C11FB470061DE29 /* NukeUI */, + 201B430F2C27F48E00879A2F /* LicenseList */, + 20B26CB52C283BE2000E1A79 /* VRCKit */, ); productName = harmonie; productReference = 20395D912B945CD700003921 /* harmonie.app */; @@ -343,9 +349,10 @@ ); mainGroup = 20395D882B945CD700003921; packageReferences = ( - 20395DC02B945E0600003921 /* XCLocalSwiftPackageReference "../vrckit" */, 206AC9022BFA087A00E0D5AE /* XCRemoteSwiftPackageReference "AsyncSwiftUI" */, 20F213D92C11FB470061DE29 /* XCRemoteSwiftPackageReference "Nuke" */, + 201B430E2C27F48E00879A2F /* XCRemoteSwiftPackageReference "LicenseList" */, + 20B26CB42C283BE2000E1A79 /* XCRemoteSwiftPackageReference "vrckit" */, ); productRefGroup = 20395D922B945CD700003921 /* Products */; projectDirPath = ""; @@ -404,10 +411,10 @@ 205FFDD52C15783F00D93ACD /* FavoriteViewModel.swift in Sources */, 201E40E52B9B55E300321521 /* LoginView.swift in Sources */, 200723972BA56377002C27E8 /* UserDetailView.swift in Sources */, - 20E3FC032C245B5300E336E3 /* HarmonieErrors.swift in Sources */, 2019D1F02B9C3CBF0094E358 /* SizeViewModifier.swift in Sources */, 209938A62C1099EE003B4A8A /* SectionModifier.swift in Sources */, 209938A82C109B08003B4A8A /* View+sectioning.swift in Sources */, + 201B430D2C27F44100879A2F /* Errors.swift in Sources */, 202844C22BA5CC4F00357D09 /* FavoritesView.swift in Sources */, 2019D1ED2B9C3AEC0094E358 /* View+frame.swift in Sources */, 205B3BF42C00D8BF0097D62A /* ProgressScreen.swift in Sources */, @@ -752,14 +759,15 @@ }; /* End XCConfigurationList section */ -/* Begin XCLocalSwiftPackageReference section */ - 20395DC02B945E0600003921 /* XCLocalSwiftPackageReference "../vrckit" */ = { - isa = XCLocalSwiftPackageReference; - relativePath = ../vrckit; - }; -/* End XCLocalSwiftPackageReference section */ - /* Begin XCRemoteSwiftPackageReference section */ + 201B430E2C27F48E00879A2F /* XCRemoteSwiftPackageReference "LicenseList" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/cybozu/LicenseList.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.7.0; + }; + }; 206AC9022BFA087A00E0D5AE /* XCRemoteSwiftPackageReference "AsyncSwiftUI" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/treastrain/AsyncSwiftUI"; @@ -768,6 +776,14 @@ version = 0.2.0; }; }; + 20B26CB42C283BE2000E1A79 /* XCRemoteSwiftPackageReference "vrckit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/makinosp/vrckit"; + requirement = { + branch = main; + kind = branch; + }; + }; 20F213D92C11FB470061DE29 /* XCRemoteSwiftPackageReference "Nuke" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kean/Nuke"; @@ -779,6 +795,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 201B430F2C27F48E00879A2F /* LicenseList */ = { + isa = XCSwiftPackageProductDependency; + package = 201B430E2C27F48E00879A2F /* XCRemoteSwiftPackageReference "LicenseList" */; + productName = LicenseList; + }; 20395DC12B945EEF00003921 /* VRCKit */ = { isa = XCSwiftPackageProductDependency; productName = VRCKit; @@ -792,6 +813,11 @@ package = 206AC9022BFA087A00E0D5AE /* XCRemoteSwiftPackageReference "AsyncSwiftUI" */; productName = AsyncSwiftUI; }; + 20B26CB52C283BE2000E1A79 /* VRCKit */ = { + isa = XCSwiftPackageProductDependency; + package = 20B26CB42C283BE2000E1A79 /* XCRemoteSwiftPackageReference "vrckit" */; + productName = VRCKit; + }; 20F213DC2C11FB470061DE29 /* NukeUI */ = { isa = XCSwiftPackageProductDependency; package = 20F213D92C11FB470061DE29 /* XCRemoteSwiftPackageReference "Nuke" */; diff --git a/harmonie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/harmonie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 201ada7..f111134 100644 --- a/harmonie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/harmonie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "bf6580c88aae929f51f7e6f7af7e4fc3e296ec3aa188dd4994a0a38d679eaa95", + "originHash" : "518edb848a434b30e6fd0544629522d366644d8774d2a0fc887638cadae8ff85", "pins" : [ { "identity" : "asyncswiftui", @@ -10,6 +10,15 @@ "version" : "0.2.0" } }, + { + "identity" : "licenselist", + "kind" : "remoteSourceControl", + "location" : "https://github.com/cybozu/LicenseList.git", + "state" : { + "revision" : "0828b88c1a6130d56ede2cc6c41ec50068a7ebd6", + "version" : "0.7.0" + } + }, { "identity" : "nuke", "kind" : "remoteSourceControl", @@ -18,7 +27,16 @@ "revision" : "2efd206503e99dd3a88a4dc433a76f98ce8cc198", "version" : "12.7.1" } + }, + { + "identity" : "vrckit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/makinosp/vrckit", + "state" : { + "branch" : "main", + "revision" : "9ff35047dac5063cc71be2acccf3494a9d838b1a" + } } ], - "version" : 3 + "version" : 2 } diff --git a/harmonie.xcodeproj/xcshareddata/xcschemes/harmonie.xcscheme b/harmonie.xcodeproj/xcshareddata/xcschemes/Harmonie Develop.xcscheme similarity index 100% rename from harmonie.xcodeproj/xcshareddata/xcschemes/harmonie.xcscheme rename to harmonie.xcodeproj/xcshareddata/xcschemes/Harmonie Develop.xcscheme diff --git a/harmonie/EnvironmentObjects/FriendViewModel.swift b/harmonie/EnvironmentObjects/FriendViewModel.swift index 5a08660..0ebb452 100644 --- a/harmonie/EnvironmentObjects/FriendViewModel.swift +++ b/harmonie/EnvironmentObjects/FriendViewModel.swift @@ -23,7 +23,7 @@ class FriendViewModel: ObservableObject { /// Fetch friends from API func fetchAllFriends() async throws { guard let user = userData.user else { - throw HarmonieError.dataError + throw Errors.dataError } async let onlineFriendsTask = FriendService.fetchFriends( client, diff --git a/harmonie/Utils/Errors.swift b/harmonie/Utils/Errors.swift new file mode 100644 index 0000000..0db5581 --- /dev/null +++ b/harmonie/Utils/Errors.swift @@ -0,0 +1,12 @@ +// +// Errors.swift +// harmonie +// +// Created by makinosp on 2024/06/23. +// + +import Foundation + +enum Errors: Error, LocalizedError { + case dataError +} diff --git a/harmonie/Views/SettingsView.swift b/harmonie/Views/SettingsView.swift index 2b70b53..9a7904d 100644 --- a/harmonie/Views/SettingsView.swift +++ b/harmonie/Views/SettingsView.swift @@ -6,13 +6,19 @@ // import AsyncSwiftUI +import LicenseList import VRCKit struct SettingsView: View { @EnvironmentObject var userData: UserData - @State var isSheetOpened = false + @State var sheetType: SheetType? let thumbnailSize = CGSize(width: 40, height: 40) + enum SheetType: Identifiable { + case userDetail, license + var id: Int { hashValue } + } + var body: some View { NavigationStack { VStack { @@ -23,15 +29,26 @@ struct SettingsView: View { } .font(.footnote) } - .padding() .navigationTitle("Settings") } - .sheet(isPresented: $isSheetOpened) { + .sheet(item: $sheetType) { sheetType in + presentSheet(sheetType) + } + } + + @ViewBuilder + func presentSheet(_ sheetType: SheetType) -> some View { + switch sheetType { + case .userDetail: if let user = userData.user { UserDetailView(id: user.id) .presentationDetents([.medium, .large]) .presentationBackground(Color(UIColor.systemGroupedBackground)) } + case .license: + licenseView + .presentationDetents([.large]) + .presentationBackground(Color(UIColor.systemGroupedBackground)) } } @@ -40,7 +57,7 @@ struct SettingsView: View { if let user = userData.user { Section(header: Text("My Profile")) { Button { - isSheetOpened.toggle() + sheetType = .userDetail } label: { HStack { CircleURLImage( @@ -53,19 +70,27 @@ struct SettingsView: View { .contentShape(Rectangle()) } } + .textCase(nil) } - Section { - Label { - Text("Support") - } icon: { - Image(systemName: "sparkle") + Section(header: Text("Open Source License Notice")) { + Link(destination: URL(string: "https://github.com/makinosp/harmonie")!) { + Label { + Text("Source Code") + } icon: { + Image(systemName: "curlybraces.square.fill") + } } - Label { - Text("About") - } icon: { - Image(systemName: "info.circle.fill") + Button { + sheetType = .license + } label: { + Label { + Text("Third Party Licence") + } icon: { + Image(systemName: "info.circle.fill") + } } } + .textCase(nil) Section { AsyncButton { await userData.logout() @@ -82,6 +107,13 @@ struct SettingsView: View { } } + var licenseView: some View { + NavigationView { + LicenseListView() + .navigationTitle("LICENSE") + } + } + var appName: String { Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "" }