Merge branch 'ios'
201
application-client/openvidu-ios/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@ -0,0 +1,552 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5807F0662C610D09000F07E5 /* ConfigureUrlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807F0652C610D09000F07E5 /* ConfigureUrlsView.swift */; };
|
||||
58FE57B12C4E637800759796 /* HttpClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FE57B02C4E637800759796 /* HttpClient.swift */; };
|
||||
58FE57B32C4E699200759796 /* TokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FE57B22C4E699200759796 /* TokenModel.swift */; };
|
||||
680FE2F227A8EF7700B6F6DB /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 680FE2F127A8EF7700B6F6DB /* SFSafeSymbols */; };
|
||||
6813CEFD2C09D2E30025091A /* Binding+OptionSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6813CEFC2C09D2E30025091A /* Binding+OptionSet.swift */; };
|
||||
6816968E2AF96240008ED486 /* Participant+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6816968D2AF96240008ED486 /* Participant+Helpers.swift */; };
|
||||
6816B1B0272D9198005ADB85 /* ParticipantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6816B1AF272D9198005ADB85 /* ParticipantView.swift */; };
|
||||
681E3F39271FC772007BB547 /* RoomContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681E3F38271FC772007BB547 /* RoomContext.swift */; };
|
||||
681E3F3F271FC795007BB547 /* Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681E3F3E271FC795007BB547 /* Custom.swift */; };
|
||||
681E3F43271FC7AD007BB547 /* RoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681E3F41271FC7AC007BB547 /* RoomView.swift */; };
|
||||
681E3F45271FC7AD007BB547 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681E3F42271FC7AD007BB547 /* ConnectView.swift */; };
|
||||
6847616427B44A1A001611BE /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6847616327B44A1A001611BE /* Bundle.swift */; };
|
||||
6867533B27A65652003707B9 /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6867533A27A65652003707B9 /* AppContext.swift */; };
|
||||
68816CC127B4D6BC00E24622 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 68816CC027B4D6BC00E24622 /* KeychainAccess */; };
|
||||
68816CC527B4DCD500E24622 /* SecureStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68816CC427B4DCD500E24622 /* SecureStore.swift */; };
|
||||
688D931A283FE244003CA647 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68B3853E271E780700711D5F /* Assets.xcassets */; };
|
||||
68B3854C271E780700711D5F /* OpenViduApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B3853C271E780600711D5F /* OpenViduApp.swift */; };
|
||||
68FBA43F2A38B49C0015853E /* LiveKit in Frameworks */ = {isa = PBXBuildFile; productRef = 68FBA43E2A38B49C0015853E /* LiveKit */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
68EA18EB27F2E91100F9AE48 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
5807F0652C610D09000F07E5 /* ConfigureUrlsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigureUrlsView.swift; sourceTree = "<group>"; };
|
||||
58FE57B02C4E637800759796 /* HttpClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpClient.swift; sourceTree = "<group>"; };
|
||||
58FE57B22C4E699200759796 /* TokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenModel.swift; sourceTree = "<group>"; };
|
||||
6813CEFC2C09D2E30025091A /* Binding+OptionSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+OptionSet.swift"; sourceTree = "<group>"; };
|
||||
6816968D2AF96240008ED486 /* Participant+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Participant+Helpers.swift"; sourceTree = "<group>"; };
|
||||
6816B1AF272D9198005ADB85 /* ParticipantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantView.swift; sourceTree = "<group>"; };
|
||||
681E3F38271FC772007BB547 /* RoomContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContext.swift; sourceTree = "<group>"; };
|
||||
681E3F3E271FC795007BB547 /* Custom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Custom.swift; sourceTree = "<group>"; };
|
||||
681E3F41271FC7AC007BB547 /* RoomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomView.swift; sourceTree = "<group>"; };
|
||||
681E3F42271FC7AD007BB547 /* ConnectView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = "<group>"; };
|
||||
683F05F4273F96B20080C7AC /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
|
||||
683F0603273FAD690080C7AC /* iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOS.entitlements; sourceTree = "<group>"; };
|
||||
6847616327B44A1A001611BE /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
||||
6865EA2527513B4500FFAFC3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6867533A27A65652003707B9 /* AppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = "<group>"; };
|
||||
68816CC427B4DCD500E24622 /* SecureStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureStore.swift; sourceTree = "<group>"; };
|
||||
68B3853C271E780600711D5F /* OpenViduApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenViduApp.swift; sourceTree = "<group>"; };
|
||||
68B3853E271E780700711D5F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
68B38543271E780700711D5F /* OpenViduIOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenViduIOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
68DEF27E2919EEFA00258494 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/ReplayKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
9E7835E62751A71500559DEC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
68B38540271E780700711D5F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
68FBA43F2A38B49C0015853E /* LiveKit in Frameworks */,
|
||||
680FE2F227A8EF7700B6F6DB /* SFSafeSymbols in Frameworks */,
|
||||
68816CC127B4D6BC00E24622 /* KeychainAccess in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
58FE57AF2C4E62FD00759796 /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
58FE57B02C4E637800759796 /* HttpClient.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58FE57B42C4FAFD600759796 /* Contexts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6867533A27A65652003707B9 /* AppContext.swift */,
|
||||
681E3F38271FC772007BB547 /* RoomContext.swift */,
|
||||
);
|
||||
path = Contexts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
681E3F47271FCB40007BB547 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
68DEF27E2919EEFA00258494 /* ReplayKit.framework */,
|
||||
9E7835E62751A71500559DEC /* CoreGraphics.framework */,
|
||||
683F05F4273F96B20080C7AC /* ReplayKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
683720D427A0640D007DA986 /* Support */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6847616327B44A1A001611BE /* Bundle.swift */,
|
||||
68816CC427B4DCD500E24622 /* SecureStore.swift */,
|
||||
6816968D2AF96240008ED486 /* Participant+Helpers.swift */,
|
||||
6813CEFC2C09D2E30025091A /* Binding+OptionSet.swift */,
|
||||
58FE57B22C4E699200759796 /* TokenModel.swift */,
|
||||
);
|
||||
path = Support;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6865EA2427513B4500FFAFC3 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6865EA2527513B4500FFAFC3 /* Info.plist */,
|
||||
683F0603273FAD690080C7AC /* iOS.entitlements */,
|
||||
);
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6884B77A2750505B00732D47 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6816B1AF272D9198005ADB85 /* ParticipantView.swift */,
|
||||
5807F0652C610D09000F07E5 /* ConfigureUrlsView.swift */,
|
||||
681E3F41271FC7AC007BB547 /* RoomView.swift */,
|
||||
681E3F42271FC7AD007BB547 /* ConnectView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
68B38536271E780600711D5F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
68B3853B271E780600711D5F /* Shared */,
|
||||
6865EA2427513B4500FFAFC3 /* iOS */,
|
||||
68B38544271E780700711D5F /* Products */,
|
||||
681E3F47271FCB40007BB547 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
68B3853B271E780600711D5F /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
58FE57B42C4FAFD600759796 /* Contexts */,
|
||||
58FE57AF2C4E62FD00759796 /* Utils */,
|
||||
683720D427A0640D007DA986 /* Support */,
|
||||
6884B77A2750505B00732D47 /* Views */,
|
||||
681E3F3E271FC795007BB547 /* Custom.swift */,
|
||||
68B3853C271E780600711D5F /* OpenViduApp.swift */,
|
||||
68B3853E271E780700711D5F /* Assets.xcassets */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
68B38544271E780700711D5F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
68B38543271E780700711D5F /* OpenViduIOS.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
68B38542271E780700711D5F /* OpenViduIOS (iOS) */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 68B38554271E780700711D5F /* Build configuration list for PBXNativeTarget "OpenViduIOS (iOS)" */;
|
||||
buildPhases = (
|
||||
68B3853F271E780700711D5F /* Sources */,
|
||||
68B38540271E780700711D5F /* Frameworks */,
|
||||
68B38541271E780700711D5F /* Resources */,
|
||||
68EA18EB27F2E91100F9AE48 /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "OpenViduIOS (iOS)";
|
||||
packageProductDependencies = (
|
||||
680FE2F127A8EF7700B6F6DB /* SFSafeSymbols */,
|
||||
68816CC027B4D6BC00E24622 /* KeychainAccess */,
|
||||
68FBA43E2A38B49C0015853E /* LiveKit */,
|
||||
);
|
||||
productName = "Multiplatform-SwiftUI (iOS)";
|
||||
productReference = 68B38543271E780700711D5F /* OpenViduIOS.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
68B38537271E780600711D5F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1330;
|
||||
LastUpgradeCheck = 1520;
|
||||
TargetAttributes = {
|
||||
68B38542271E780700711D5F = {
|
||||
CreatedOnToolsVersion = 13.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 68B3853A271E780600711D5F /* Build configuration list for PBXProject "OpenViduIOS" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 68B38536271E780600711D5F;
|
||||
packageReferences = (
|
||||
685271E727407BBC006B4D6A /* XCRemoteSwiftPackageReference "swift-protobuf" */,
|
||||
680FE2F027A8EF7700B6F6DB /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
|
||||
68816CBF27B4D6BC00E24622 /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||
68FBA43D2A38B49C0015853E /* XCRemoteSwiftPackageReference "client-sdk-swift" */,
|
||||
);
|
||||
productRefGroup = 68B38544271E780700711D5F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
68B38542271E780700711D5F /* OpenViduIOS (iOS) */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
68B38541271E780700711D5F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
688D931A283FE244003CA647 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
68B3853F271E780700711D5F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
681E3F45271FC7AD007BB547 /* ConnectView.swift in Sources */,
|
||||
6867533B27A65652003707B9 /* AppContext.swift in Sources */,
|
||||
5807F0662C610D09000F07E5 /* ConfigureUrlsView.swift in Sources */,
|
||||
681E3F39271FC772007BB547 /* RoomContext.swift in Sources */,
|
||||
681E3F43271FC7AD007BB547 /* RoomView.swift in Sources */,
|
||||
6816B1B0272D9198005ADB85 /* ParticipantView.swift in Sources */,
|
||||
68816CC527B4DCD500E24622 /* SecureStore.swift in Sources */,
|
||||
6813CEFD2C09D2E30025091A /* Binding+OptionSet.swift in Sources */,
|
||||
6816968E2AF96240008ED486 /* Participant+Helpers.swift in Sources */,
|
||||
68B3854C271E780700711D5F /* OpenViduApp.swift in Sources */,
|
||||
58FE57B12C4E637800759796 /* HttpClient.swift in Sources */,
|
||||
58FE57B32C4E699200759796 /* TokenModel.swift in Sources */,
|
||||
681E3F3F271FC795007BB547 /* Custom.swift in Sources */,
|
||||
6847616427B44A1A001611BE /* Bundle.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
68B38552271E780700711D5F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"GL_SILENCE_DEPRECATION=1",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
68B38553271E780700711D5F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "GL_SILENCE_DEPRECATION=1";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
68B38555271E780700711D5F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = iOS;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOS/iOS.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.0;
|
||||
DEVELOPMENT_TEAM = N5K98M7Q94;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = iOS/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "OpenVidu iOS";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "Please allow Camera access";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Please allow Microphone access";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.openvidu.ios;
|
||||
PRODUCT_NAME = OpenViduIOS;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
68B38556271E780700711D5F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = iOS;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOS/iOS.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.0;
|
||||
DEVELOPMENT_TEAM = N5K98M7Q94;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = iOS/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "OpenVidu iOS";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "Please allow Camera access";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Please allow Microphone access";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.openvidu.ios;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = io.test.prueba;
|
||||
"PRODUCT_BUNDLE_IDENTIFIER[sdk=macosx*]" = io.test.prueba;
|
||||
PRODUCT_NAME = OpenViduIOS;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
68B3853A271E780600711D5F /* Build configuration list for PBXProject "OpenViduIOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
68B38552271E780700711D5F /* Debug */,
|
||||
68B38553271E780700711D5F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
68B38554271E780700711D5F /* Build configuration list for PBXNativeTarget "OpenViduIOS (iOS)" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
68B38555271E780700711D5F /* Debug */,
|
||||
68B38556271E780700711D5F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
680FE2F027A8EF7700B6F6DB /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.3.0;
|
||||
};
|
||||
};
|
||||
685271E727407BBC006B4D6A /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/apple/swift-protobuf.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.26.0;
|
||||
};
|
||||
};
|
||||
68816CBF27B4D6BC00E24622 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 4.2.2;
|
||||
};
|
||||
};
|
||||
68FBA43D2A38B49C0015853E /* XCRemoteSwiftPackageReference "client-sdk-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/livekit/client-sdk-swift";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 2.0.11;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
680FE2F127A8EF7700B6F6DB /* SFSafeSymbols */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 680FE2F027A8EF7700B6F6DB /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
|
||||
productName = SFSafeSymbols;
|
||||
};
|
||||
68816CC027B4D6BC00E24622 /* KeychainAccess */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 68816CBF27B4D6BC00E24622 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
||||
productName = KeychainAccess;
|
||||
};
|
||||
68FBA43E2A38B49C0015853E /* LiveKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 68FBA43D2A38B49C0015853E /* XCRemoteSwiftPackageReference "client-sdk-swift" */;
|
||||
productName = LiveKit;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 68B38537271E780600711D5F /* Project object */;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -0,0 +1,8 @@
|
||||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,60 @@
|
||||
{
|
||||
"originHash" : "bf59b79f5d4ea0a54ef7e929b035c5821f5094ee990a7a369ad0cb3713f6c62d",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "client-sdk-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/livekit/client-sdk-swift",
|
||||
"state" : {
|
||||
"revision" : "367d95fc9a16bab88c3ec73e019726f4608b2c18",
|
||||
"version" : "2.0.11"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "keychainaccess",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
|
||||
"state" : {
|
||||
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||
"version" : "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sfsafesymbols",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SFSafeSymbols/SFSafeSymbols",
|
||||
"state" : {
|
||||
"revision" : "e2e28f4e56e1769c2ec3c61c9355fc64eb7a535a",
|
||||
"version" : "5.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log.git",
|
||||
"state" : {
|
||||
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
|
||||
"version" : "1.6.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "d57a5aecf24a25b32ec4a74be2f5d0a995a47c4b",
|
||||
"version" : "1.27.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "webrtc-xcframework",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/livekit/webrtc-xcframework.git",
|
||||
"state" : {
|
||||
"revision" : "5640582dbac33b193608574d4aa2a8d5d825db74",
|
||||
"version" : "125.6422.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "683F05F2273F96B20080C7AC"
|
||||
BuildableName = "BroadcastExt.appex"
|
||||
BlueprintName = "BroadcastExt"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableThreadSanitizer = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "68B38542271E780700711D5F"
|
||||
BuildableName = "OpenViduIOS.app"
|
||||
BlueprintName = "OpenViduIOS (iOS)"
|
||||
ReferencedContainer = "container:OpenViduIOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "0172B4AF-56D2-4502-B8CB-639CB2352BDE"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "06F60038-E3C6-4430-A62E-71C881C0E0E7"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Shared/LiveKitExample.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "105"
|
||||
endingLineNumber = "105"
|
||||
landmarkName = "body"
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "D95985CD-AB9F-47D1-90F0-0D50804A0AD0"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Shared/Controllers/RoomContext.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "164"
|
||||
endingLineNumber = "164"
|
||||
landmarkName = "connect(entry:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "7725A61B-C57C-42BD-AA7D-AE98DA6F1755"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Shared/ConnectView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "150"
|
||||
endingLineNumber = "150"
|
||||
landmarkName = "connectToRoom()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
23
application-client/openvidu-ios/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Basic iOS
|
||||
|
||||
Basic client application built for iOS using Swift. It internally uses [LiveKit Swift SDK (iOS/macOS)](https://github.com/livekit/client-sdk-swift).
|
||||
|
||||
For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/tutorials/application-client/ios/).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Xcode](https://apps.apple.com/es/app/xcode/id497799835?mt=12)
|
||||
|
||||
## Run
|
||||
|
||||
1. Download repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git
|
||||
```
|
||||
|
||||
2. Launch Xcode and open `OpenViduIOS.xcodeproj`
|
||||
|
||||
3. **Run** the project from the menu **Product** → **Run** or by ⌘R.
|
||||
|
||||
If you encounter code signing issues, make sure you change the **Team** and **bundle id** from the previous step.
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xAA",
|
||||
"green" : "0x88",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4D",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4D"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x29",
|
||||
"green" : "0x27",
|
||||
"red" : "0x1E"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0xCB",
|
||||
"red" : "0xFE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-20x20@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-29x29@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-40x40@2x 1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "ios.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 664 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 928 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 30 KiB |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
23
application-client/openvidu-ios/Shared/Assets.xcassets/Images/logo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "logo.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "logo 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "logo 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
application-client/openvidu-ios/Shared/Assets.xcassets/Images/logo.imageset/logo 1.png
vendored
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
application-client/openvidu-ios/Shared/Assets.xcassets/Images/logo.imageset/logo 2.png
vendored
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
application-client/openvidu-ios/Shared/Assets.xcassets/Images/logo.imageset/logo.png
vendored
Normal file
|
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2024 OpenVidu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Combine
|
||||
import LiveKit
|
||||
import SwiftUI
|
||||
|
||||
// This class contains the logic to control behavior of the whole app.
|
||||
final class AppContext: ObservableObject {
|
||||
private let store: ValueStore<Preferences>
|
||||
|
||||
@Published var applicationServerUrl: String = "" {
|
||||
didSet { store.value.applicationServerUrl = applicationServerUrl }
|
||||
}
|
||||
|
||||
public init(store: ValueStore<Preferences>) {
|
||||
self.store = store
|
||||
|
||||
applicationServerUrl = store.value.applicationServerUrl
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LiveKit
|
||||
import SwiftUI
|
||||
|
||||
// This class contains the logic to control behavior of the room.
|
||||
final class RoomContext: ObservableObject {
|
||||
let jsonEncoder = JSONEncoder()
|
||||
let jsonDecoder = JSONDecoder()
|
||||
|
||||
private let store: ValueStore<Preferences>
|
||||
|
||||
// Used to show connection error dialog
|
||||
@Published var shouldShowDisconnectReason: Bool = false
|
||||
public var latestError: LiveKitError?
|
||||
|
||||
public let room = Room()
|
||||
|
||||
@Published var livekitUrl: String = "" {
|
||||
didSet { store.value.livekitUrl = livekitUrl }
|
||||
}
|
||||
|
||||
@Published var name: String = "" {
|
||||
didSet { store.value.name = name }
|
||||
}
|
||||
|
||||
@Published var localParticipantName: String = "" {
|
||||
didSet { store.value.localParticipantName = localParticipantName }
|
||||
}
|
||||
|
||||
@Published var token: String = "" {
|
||||
didSet { store.value.token = token }
|
||||
}
|
||||
|
||||
@Published var textFieldString: String = ""
|
||||
|
||||
var _connectTask: Task<Void, Error>?
|
||||
|
||||
public init(store: ValueStore<Preferences>) {
|
||||
self.store = store
|
||||
room.add(delegate: self)
|
||||
|
||||
livekitUrl = store.value.livekitUrl
|
||||
name = store.value.name
|
||||
token = store.value.token
|
||||
localParticipantName = store.value.localParticipantName
|
||||
|
||||
#if os(iOS)
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
#endif
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if os(iOS)
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
#endif
|
||||
print("RoomContext.deinit")
|
||||
}
|
||||
|
||||
func cancelConnect() {
|
||||
_connectTask?.cancel()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func connect() async throws -> Room {
|
||||
|
||||
let connectTask = Task.detached { [weak self] in
|
||||
guard let self else { return }
|
||||
try await self.room.connect(url: self.livekitUrl,token: self.token)
|
||||
}
|
||||
|
||||
_connectTask = connectTask
|
||||
try await connectTask.value
|
||||
|
||||
return room
|
||||
}
|
||||
|
||||
func disconnect() async {
|
||||
await room.disconnect()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension RoomContext: RoomDelegate {
|
||||
|
||||
func room(_ room: Room, didUpdateConnectionState connectionState: ConnectionState, from oldValue: ConnectionState) {
|
||||
print("Did update connectionState \(oldValue) -> \(connectionState)")
|
||||
|
||||
if case .disconnected = connectionState,
|
||||
let error = room.disconnectError,
|
||||
error.type != .cancelled
|
||||
{
|
||||
latestError = room.disconnectError
|
||||
|
||||
Task.detached { @MainActor [weak self] in
|
||||
guard let self else { return }
|
||||
self.shouldShowDisconnectReason = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
114
application-client/openvidu-ios/Shared/Custom.swift
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Color {
|
||||
static let ovYellow = Color("OVYellow")
|
||||
static let ovBlue = Color("OVBlue")
|
||||
static let ovGray = Color("OVGray")
|
||||
static let ovGray2 = Color("OVGray2")
|
||||
}
|
||||
|
||||
struct LazyView<Content: View>: View {
|
||||
let build: () -> Content
|
||||
init(_ build: @autoclosure @escaping () -> Content) {
|
||||
self.build = build
|
||||
}
|
||||
|
||||
var body: Content {
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
// Default button style for this example
|
||||
struct LKButton: View {
|
||||
let title: String
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action,
|
||||
label: {
|
||||
Text(title.uppercased())
|
||||
.fontWeight(.bold)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
})
|
||||
.background(Color.ovBlue)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
extension LKTextField.`Type` {
|
||||
func toiOSType() -> UIKeyboardType {
|
||||
switch self {
|
||||
case .URL: return .URL
|
||||
case .ascii: return .asciiCapable
|
||||
default: return .default
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
// Avoid showing focus border around textfield for macOS
|
||||
extension NSTextField {
|
||||
override open var focusRingType: NSFocusRingType {
|
||||
get { .none }
|
||||
set {}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct LKTextField: View {
|
||||
enum `Type` {
|
||||
case `default`
|
||||
case URL
|
||||
case ascii
|
||||
case secret
|
||||
}
|
||||
|
||||
let title: String
|
||||
@Binding var text: String
|
||||
var type: Type = .default
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10.0) {
|
||||
Text(title)
|
||||
.fontWeight(.bold)
|
||||
|
||||
Group {
|
||||
if type == .secret {
|
||||
SecureField("", text: $text)
|
||||
} else {
|
||||
TextField("", text: $text)
|
||||
}
|
||||
}
|
||||
.textFieldStyle(.plain)
|
||||
.disableAutocorrection(true)
|
||||
.padding()
|
||||
.overlay(RoundedRectangle(cornerRadius: 10.0)
|
||||
.strokeBorder(Color.white.opacity(0.3),
|
||||
style: StrokeStyle(lineWidth: 1.0)))
|
||||
#if os(iOS)
|
||||
.autocapitalization(.none)
|
||||
.keyboardType(type.toiOSType())
|
||||
#endif
|
||||
|
||||
}.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
188
application-client/openvidu-ios/Shared/OpenViduApp.swift
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import KeychainAccess
|
||||
import LiveKit
|
||||
import Logging
|
||||
import SwiftUI
|
||||
|
||||
let sync = ValueStore<Preferences>(store: Keychain(service: "io.openvidu.ios"),
|
||||
key: "preferences",
|
||||
default: Preferences())
|
||||
|
||||
struct RoomSwitchView: View {
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
@EnvironmentObject var roomCtx: RoomContext
|
||||
@EnvironmentObject var room: Room
|
||||
|
||||
var shouldShowRoomView: Bool {
|
||||
room.connectionState == .connected || room.connectionState == .reconnecting
|
||||
}
|
||||
|
||||
var shouldShowConfigureUrlsView: Bool {
|
||||
appCtx.applicationServerUrl.isEmpty || roomCtx.livekitUrl.isEmpty
|
||||
|
||||
}
|
||||
|
||||
func computeTitle() -> String {
|
||||
if shouldShowRoomView {
|
||||
var elements: [String] = []
|
||||
if let roomName = room.name {
|
||||
elements.append(roomName)
|
||||
}
|
||||
if let localParticipantName = room.localParticipant.name {
|
||||
elements.append(localParticipantName)
|
||||
}
|
||||
if let localParticipantIdentity = room.localParticipant.identity {
|
||||
elements.append(String(describing: localParticipantIdentity))
|
||||
}
|
||||
return elements.joined(separator: " ")
|
||||
}
|
||||
|
||||
return "OpenVidu"
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.black
|
||||
.ignoresSafeArea()
|
||||
|
||||
if shouldShowRoomView {
|
||||
RoomView()
|
||||
} else {
|
||||
if shouldShowConfigureUrlsView {
|
||||
ConfigureUrlsView()
|
||||
} else {
|
||||
ConnectView()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(computeTitle())
|
||||
}
|
||||
}
|
||||
|
||||
// Attaches RoomContext and Room to the environment
|
||||
struct RoomContextView: View {
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
@StateObject var roomCtx = RoomContext(store: sync)
|
||||
|
||||
var body: some View {
|
||||
RoomSwitchView()
|
||||
.environmentObject(roomCtx)
|
||||
.environmentObject(roomCtx.room)
|
||||
.environment(\.colorScheme, .dark)
|
||||
.foregroundColor(Color.white)
|
||||
.onDisappear {
|
||||
print("\(String(describing: type(of: self))) onDisappear")
|
||||
Task {
|
||||
await roomCtx.disconnect()
|
||||
}
|
||||
}
|
||||
.onOpenURL(perform: { url in
|
||||
|
||||
guard let urlComponent = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
|
||||
guard let host = url.host else { return }
|
||||
|
||||
let secureValue = urlComponent.queryItems?.first(where: { $0.name == "secure" })?.value?.lowercased()
|
||||
let secure = ["true", "1"].contains { $0 == secureValue }
|
||||
|
||||
let tokenValue = urlComponent.queryItems?.first(where: { $0.name == "token" })?.value ?? ""
|
||||
|
||||
var builder = URLComponents()
|
||||
builder.scheme = secure ? "wss" : "ws"
|
||||
builder.host = host
|
||||
builder.port = url.port
|
||||
|
||||
guard let builtUrl = builder.url?.absoluteString else { return }
|
||||
|
||||
print("built URL: \(builtUrl), token: \(tokenValue)")
|
||||
|
||||
Task { @MainActor in
|
||||
roomCtx.livekitUrl = builtUrl
|
||||
roomCtx.token = tokenValue
|
||||
if !roomCtx.token.isEmpty {
|
||||
try await roomCtx.connect()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension Decimal {
|
||||
mutating func round(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) {
|
||||
var localCopy = self
|
||||
NSDecimalRound(&self, &localCopy, scale, roundingMode)
|
||||
}
|
||||
|
||||
func rounded(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) -> Decimal {
|
||||
var result = Decimal()
|
||||
var localCopy = self
|
||||
NSDecimalRound(&result, &localCopy, scale, roundingMode)
|
||||
return result
|
||||
}
|
||||
|
||||
func remainder(of divisor: Decimal) -> Decimal {
|
||||
let s = self as NSDecimalNumber
|
||||
let d = divisor as NSDecimalNumber
|
||||
let b = NSDecimalNumberHandler(roundingMode: .down,
|
||||
scale: 0,
|
||||
raiseOnExactness: false,
|
||||
raiseOnOverflow: false,
|
||||
raiseOnUnderflow: false,
|
||||
raiseOnDivideByZero: false)
|
||||
let quotient = s.dividing(by: d, withBehavior: b)
|
||||
|
||||
let subtractAmount = quotient.multiplying(by: d)
|
||||
return s.subtracting(subtractAmount) as Decimal
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct OpenViduApp: App {
|
||||
@StateObject var appCtx = AppContext(store: sync)
|
||||
|
||||
func nearestSafeScale(for target: Int, scale: Double) -> Decimal {
|
||||
let p = Decimal(sign: .plus, exponent: -3, significand: 1)
|
||||
let t = Decimal(target)
|
||||
var s = Decimal(scale).rounded(3, .down)
|
||||
|
||||
while (t * s / 2).remainder(of: 2) != 0 {
|
||||
s = s + p
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
init() {
|
||||
LoggingSystem.bootstrap {
|
||||
var logHandler = StreamLogHandler.standardOutput(label: $0)
|
||||
logHandler.logLevel = .debug
|
||||
return logHandler
|
||||
}
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RoomContextView()
|
||||
.environmentObject(appCtx)
|
||||
}
|
||||
.handlesExternalEvents(matching: Set(arrayLiteral: "*"))
|
||||
#if os(macOS)
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
.windowToolbarStyle(.unifiedCompact)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Binding where Value: OptionSet, Value == Value.Element {
|
||||
func bindedValue(_ options: Value) -> Bool {
|
||||
wrappedValue.contains(options)
|
||||
}
|
||||
|
||||
func bind(
|
||||
_ options: Value,
|
||||
animate: Bool = false
|
||||
) -> Binding<Bool> {
|
||||
.init { () -> Bool in
|
||||
self.wrappedValue.contains(options)
|
||||
} set: { newValue in
|
||||
let body = {
|
||||
if newValue {
|
||||
self.wrappedValue.insert(options)
|
||||
} else {
|
||||
self.wrappedValue.remove(options)
|
||||
}
|
||||
}
|
||||
guard animate else {
|
||||
body()
|
||||
return
|
||||
}
|
||||
withAnimation {
|
||||
body()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
application-client/openvidu-ios/Shared/Support/Bundle.swift
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Bundle {
|
||||
var appName: String { getInfo("CFBundleName") }
|
||||
var displayName: String { getInfo("CFBundleDisplayName") }
|
||||
var language: String { getInfo("CFBundleDevelopmentRegion") }
|
||||
var identifier: String { getInfo("CFBundleIdentifier") }
|
||||
|
||||
var appBuild: String { getInfo("CFBundleVersion") }
|
||||
var appVersionLong: String { getInfo("CFBundleShortVersionString") }
|
||||
var appVersionShort: String { getInfo("CFBundleShortVersion") }
|
||||
|
||||
private func getInfo(_ str: String) -> String { infoDictionary?[str] as? String ?? "⚠️" }
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LiveKit
|
||||
|
||||
public extension Participant {
|
||||
var mainVideoPublication: TrackPublication? {
|
||||
firstCameraPublication
|
||||
}
|
||||
|
||||
var mainVideoTrack: VideoTrack? {
|
||||
firstCameraVideoTrack
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Combine
|
||||
import KeychainAccess
|
||||
import LiveKit
|
||||
import SwiftUI
|
||||
|
||||
struct Preferences: Codable, Equatable {
|
||||
// General
|
||||
var applicationServerUrl = ""
|
||||
|
||||
// Room
|
||||
var livekitUrl = ""
|
||||
var name = ""
|
||||
var token = ""
|
||||
var localParticipantName = ""
|
||||
|
||||
}
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
class ValueStore<T: Codable & Equatable>: ObservableObject {
|
||||
private let store: Keychain
|
||||
private let key: String
|
||||
private let message = ""
|
||||
private weak var timer: Timer?
|
||||
|
||||
public var value: T {
|
||||
didSet {
|
||||
guard oldValue != value else { return }
|
||||
lazySync()
|
||||
}
|
||||
}
|
||||
|
||||
private var storeWithOptions: Keychain {
|
||||
store
|
||||
.accessibility(.whenUnlocked)
|
||||
.synchronizable(true)
|
||||
}
|
||||
|
||||
public init(store: Keychain, key: String, default: T) {
|
||||
self.store = store
|
||||
self.key = key
|
||||
value = `default`
|
||||
|
||||
if let data = try? storeWithOptions.getData(key),
|
||||
let result = try? decoder.decode(T.self, from: data)
|
||||
{
|
||||
value = result
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
timer?.invalidate()
|
||||
}
|
||||
|
||||
public func lazySync() {
|
||||
timer?.invalidate()
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 1,
|
||||
repeats: false,
|
||||
block: { _ in self.sync() })
|
||||
}
|
||||
|
||||
public func sync() {
|
||||
do {
|
||||
let data = try encoder.encode(value)
|
||||
try storeWithOptions.set(data, key: key)
|
||||
} catch {
|
||||
print("Failed to write in Keychain, error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2024 OpenVidu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
struct TokenRequest: Codable {
|
||||
let roomName: String
|
||||
let participantName: String
|
||||
}
|
||||
|
||||
struct TokenResponse: Codable {
|
||||
let token: String
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2024 OpenVidu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
class HTTPClient {
|
||||
|
||||
func getToken(applicationServerUrl: String, roomName: String, participantName: String) async throws -> String {
|
||||
guard let url = URL(string: "\(applicationServerUrl)/token") else {
|
||||
throw URLError(.badURL)
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let tokenRequest = TokenRequest(roomName: roomName, participantName: participantName)
|
||||
let jsonData = try JSONEncoder().encode(tokenRequest)
|
||||
request.httpBody = jsonData
|
||||
|
||||
do {
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
|
||||
guard httpResponse.statusCode == 200 else {
|
||||
let errorResponse = try? JSONDecoder().decode([String: String].self, from: data)
|
||||
let errorMessage = errorResponse?["errorMessage"] ?? "Unknown error"
|
||||
throw NSError(domain: "HTTPServiceError", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP Error: \(errorMessage)"])
|
||||
}
|
||||
|
||||
let tokenResponse = try JSONDecoder().decode(TokenResponse.self, from: data)
|
||||
return tokenResponse.token
|
||||
|
||||
} catch {
|
||||
print("Error occurred: \(error.localizedDescription)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2024 OpenVidu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import LiveKit
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
struct ConfigureUrlsView: View {
|
||||
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
@EnvironmentObject var roomCtx: RoomContext
|
||||
|
||||
@State private var applicationServerUrl: String = ""
|
||||
@State private var livekitUrl: String = ""
|
||||
@State private var errorMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
Color.ovGray
|
||||
.ignoresSafeArea()
|
||||
ScrollView {
|
||||
VStack(alignment: .center, spacing: 40.0) {
|
||||
|
||||
Text("Configure the LiveKit URL and the Application Server URL before connecting to the Room")
|
||||
|
||||
VStack(spacing: 15) {
|
||||
LKTextField(title: "Application Server URL", text: $applicationServerUrl, type: .URL)
|
||||
LKTextField(title: "LiveKit URL", text: $livekitUrl, type: .URL)
|
||||
}
|
||||
.frame(maxWidth: 350)
|
||||
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
|
||||
LKButton(title: "Save") {
|
||||
Task.detached { @MainActor in
|
||||
let isApplicationServerValid = isValidURL(self.applicationServerUrl)
|
||||
let isLivekitUrlValid = isValidURL(self.livekitUrl)
|
||||
|
||||
if !isApplicationServerValid || !isLivekitUrlValid {
|
||||
print("Invalid URLs")
|
||||
errorMessage = "There was an error with the url values"
|
||||
return
|
||||
}
|
||||
appCtx.applicationServerUrl = self.applicationServerUrl
|
||||
roomCtx.livekitUrl = self.livekitUrl
|
||||
errorMessage = ""
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Text(errorMessage)
|
||||
.foregroundColor(.red)
|
||||
|
||||
|
||||
}
|
||||
.padding()
|
||||
.frame(width: geometry.size.width) // Make the scroll view full-width
|
||||
.frame(minHeight: geometry.size.height) // Set the content’s min height to the parent
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
.frame(minWidth: 500, minHeight: 500)
|
||||
#endif
|
||||
.alert(isPresented: $roomCtx.shouldShowDisconnectReason) {
|
||||
Alert(title: Text("Disconnected"),
|
||||
message: Text("Reason: " + String(describing: roomCtx.latestError)))
|
||||
}
|
||||
}
|
||||
|
||||
func isValidURL(_ urlString: String) -> Bool {
|
||||
guard let url = URL(string: urlString),
|
||||
let scheme = url.scheme else {
|
||||
return false
|
||||
}
|
||||
return ["http", "https", "ws", "wss"].contains(scheme)
|
||||
}
|
||||
}
|
||||
125
application-client/openvidu-ios/Shared/Views/ConnectView.swift
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import LiveKit
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
struct ConnectView: View {
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
@EnvironmentObject var roomCtx: RoomContext
|
||||
@EnvironmentObject var room: Room
|
||||
|
||||
var httpService = HTTPClient()
|
||||
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
Color.ovGray
|
||||
.ignoresSafeArea()
|
||||
ScrollView {
|
||||
VStack(alignment: .center, spacing: 40.0) {
|
||||
VStack(spacing: 10) {
|
||||
Image("logo")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 50)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
|
||||
VStack(spacing: 15) {
|
||||
LKTextField(title: "Participant Name", text: $roomCtx.localParticipantName, type: .ascii)
|
||||
LKTextField(title: "Room Name", text: $roomCtx.name, type: .ascii)
|
||||
}
|
||||
.frame(maxWidth: 350)
|
||||
|
||||
if case .connecting = room.connectionState {
|
||||
HStack(alignment: .center) {
|
||||
ProgressView()
|
||||
|
||||
LKButton(title: "Cancel") {
|
||||
roomCtx.cancelConnect()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HStack(alignment: .center) {
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
LKButton(title: "Reset urls") {
|
||||
Task.detached { @MainActor in
|
||||
roomCtx.livekitUrl = ""
|
||||
appCtx.applicationServerUrl = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LKButton(title: "Connect") {
|
||||
Task.detached { @MainActor in
|
||||
await connectToRoom()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(width: geometry.size.width) // Make the scroll view full-width
|
||||
.frame(minHeight: geometry.size.height) // Set the content’s min height to the parent
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
.frame(minWidth: 500, minHeight: 500)
|
||||
#endif
|
||||
.alert(isPresented: $roomCtx.shouldShowDisconnectReason) {
|
||||
Alert(title: Text("Disconnected"),
|
||||
message: Text("Reason: " + String(describing: roomCtx.latestError)))
|
||||
}
|
||||
}
|
||||
|
||||
func connectToRoom() async {
|
||||
let livekitUrl = roomCtx.livekitUrl
|
||||
let roomName = roomCtx.name
|
||||
let participantName = roomCtx.localParticipantName
|
||||
let applicationServerUrl = appCtx.applicationServerUrl
|
||||
|
||||
guard !livekitUrl.isEmpty, !roomName.isEmpty else {
|
||||
print("LiveKit URL or room name is empty")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let token = try await httpService.getToken(applicationServerUrl: applicationServerUrl, roomName: roomName, participantName: participantName)
|
||||
print("Token received: \(token)")
|
||||
|
||||
if token.isEmpty {
|
||||
print("Received empty token")
|
||||
return
|
||||
}
|
||||
|
||||
roomCtx.token = token
|
||||
print("Connecting to room...")
|
||||
try await roomCtx.connect()
|
||||
print("Room connected")
|
||||
} catch {
|
||||
print("Failed to get token: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LiveKit
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
struct ParticipantView: View {
|
||||
@ObservedObject var participant: Participant
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
|
||||
var videoViewMode: VideoView.LayoutMode = .fill
|
||||
|
||||
@State private var isRendering: Bool = false
|
||||
|
||||
func bgView(systemSymbol: SFSymbol, geometry: GeometryProxy) -> some View {
|
||||
Image(systemSymbol: systemSymbol)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.foregroundColor(Color.ovGray2)
|
||||
.frame(width: min(geometry.size.width, geometry.size.height) * 0.3)
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
|
||||
ZStack(alignment: .bottom) {
|
||||
// Background color
|
||||
Color.ovGray
|
||||
.ignoresSafeArea()
|
||||
|
||||
// VideoView for the Participant
|
||||
if let publication = participant.mainVideoPublication,
|
||||
!publication.isMuted,
|
||||
let track = publication.track as? VideoTrack
|
||||
{
|
||||
ZStack(alignment: .topLeading) {
|
||||
SwiftUIVideoView(track,
|
||||
layoutMode: videoViewMode,
|
||||
isRendering: $isRendering)
|
||||
|
||||
if !isRendering {
|
||||
ProgressView().progressViewStyle(CircularProgressViewStyle())
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||
}
|
||||
|
||||
// Display participant name in the top leading corner
|
||||
if let participantName = participant.identity?.description {
|
||||
Text(participantName) // Assuming `participant` has a `name` property
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.padding(8)
|
||||
.background(Color.black.opacity(0.6))
|
||||
.cornerRadius(8)
|
||||
.padding([.top, .leading], 16) // Adjust padding as needed
|
||||
}
|
||||
|
||||
}
|
||||
} else if let publication = participant.mainVideoPublication as? RemoteTrackPublication,
|
||||
case .notAllowed = publication.subscriptionState
|
||||
{
|
||||
// Show no permission icon
|
||||
bgView(systemSymbol: .exclamationmarkCircle, geometry: geometry)
|
||||
} else {
|
||||
// Show no camera icon
|
||||
ZStack(alignment: .topLeading) {
|
||||
// Display participant name in the top leading corner
|
||||
bgView(systemSymbol: .videoSlashFill, geometry: geometry)
|
||||
|
||||
if let participantName = participant.identity?.description {
|
||||
Text(participantName) // Assuming `participant` has a `name` property
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.padding(8)
|
||||
.background(Color.black.opacity(0.6))
|
||||
.cornerRadius(8)
|
||||
.padding([.top, .leading], 16) // Adjust padding as needed
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
385
application-client/openvidu-ios/Shared/Views/RoomView.swift
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright 2024 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LiveKit
|
||||
import SFSafeSymbols
|
||||
import SwiftUI
|
||||
|
||||
#if !os(macOS)
|
||||
let adaptiveMin = 170.0
|
||||
let toolbarPlacement: ToolbarItemPlacement = .bottomBar
|
||||
#else
|
||||
let adaptiveMin = 300.0
|
||||
let toolbarPlacement: ToolbarItemPlacement = .primaryAction
|
||||
#endif
|
||||
|
||||
extension CIImage {
|
||||
// helper to create a `CIImage` for both platforms
|
||||
convenience init(named name: String) {
|
||||
#if !os(macOS)
|
||||
self.init(cgImage: UIImage(named: name)!.cgImage!)
|
||||
#else
|
||||
self.init(data: NSImage(named: name)!.tiffRepresentation!)!
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
// keeps weak reference to NSWindow
|
||||
class WindowAccess: ObservableObject {
|
||||
private weak var window: NSWindow?
|
||||
|
||||
deinit {
|
||||
// reset changed properties
|
||||
DispatchQueue.main.async { [weak window] in
|
||||
window?.level = .normal
|
||||
}
|
||||
}
|
||||
|
||||
@Published public var pinned: Bool = false {
|
||||
didSet {
|
||||
guard oldValue != pinned else { return }
|
||||
level = pinned ? .floating : .normal
|
||||
}
|
||||
}
|
||||
|
||||
private var level: NSWindow.Level {
|
||||
get { window?.level ?? .normal }
|
||||
set {
|
||||
Task { @MainActor in
|
||||
window?.level = newValue
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func set(window: NSWindow?) {
|
||||
self.window = window
|
||||
Task { @MainActor in
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct RoomView: View {
|
||||
@EnvironmentObject var appCtx: AppContext
|
||||
@EnvironmentObject var roomCtx: RoomContext
|
||||
@EnvironmentObject var room: Room
|
||||
|
||||
@State var isCameraPublishingBusy = false
|
||||
@State var isMicrophonePublishingBusy = false
|
||||
@State var isScreenSharePublishingBusy = false
|
||||
|
||||
@State private var screenPickerPresented = false
|
||||
@State private var publishOptionsPickerPresented = false
|
||||
|
||||
@State private var cameraPublishOptions = VideoPublishOptions()
|
||||
|
||||
#if os(macOS)
|
||||
@ObservedObject private var windowAccess = WindowAccess()
|
||||
#endif
|
||||
|
||||
func sortedParticipants() -> [Participant] {
|
||||
room.allParticipants.values.sorted { p1, p2 in
|
||||
if p1 is LocalParticipant { return true }
|
||||
if p2 is LocalParticipant { return false }
|
||||
return (p1.joinedAt ?? Date()) < (p2.joinedAt ?? Date())
|
||||
}
|
||||
}
|
||||
|
||||
func content(geometry: GeometryProxy) -> some View {
|
||||
VStack {
|
||||
|
||||
HStack {
|
||||
// Title Text
|
||||
Text(roomCtx.name)
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
|
||||
Spacer() // Pushes the button to the right
|
||||
Button(action: {
|
||||
Task {
|
||||
await roomCtx.disconnect()
|
||||
}
|
||||
}, label: {
|
||||
HStack {
|
||||
Image(systemSymbol: .xmarkCircleFill)
|
||||
.renderingMode(.original)
|
||||
Text("Leave Room")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color.red.opacity(0.8)) // Background color for the button
|
||||
.foregroundColor(.white) // Text color
|
||||
.cornerRadius(8)
|
||||
})
|
||||
//.padding()
|
||||
}
|
||||
|
||||
// Re-connecting Status
|
||||
if case .connecting = room.connectionState {
|
||||
Text("Re-connecting...")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
.background(Color.black.opacity(0.6))
|
||||
.cornerRadius(8)
|
||||
.padding(.bottom)
|
||||
}
|
||||
// Participant layout
|
||||
HorVStack(axis: geometry.isTall ? .vertical : .horizontal, spacing: 5) {
|
||||
Group {
|
||||
ParticipantLayout(sortedParticipants(), spacing: 5) { participant in
|
||||
ParticipantView(participant: participant, videoViewMode: .fill)
|
||||
}
|
||||
}
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 0,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
content(geometry: geometry)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: toolbarPlacement) {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
let isMicrophoneEnabled = room.localParticipant.isMicrophoneEnabled()
|
||||
let isCameraEnabled = room.localParticipant.isCameraEnabled()
|
||||
|
||||
Button(action: {
|
||||
Task {
|
||||
isCameraPublishingBusy = true
|
||||
defer { Task { @MainActor in isCameraPublishingBusy = false } }
|
||||
do {
|
||||
try await room.localParticipant.setCamera(enabled: !isCameraEnabled)
|
||||
} catch {
|
||||
print("Failed to toggle camera: \(error)")
|
||||
}
|
||||
}
|
||||
}, label: {
|
||||
Image(systemSymbol: isCameraEnabled ? .videoFill : .videoSlashFill)
|
||||
.renderingMode(isCameraEnabled ? .original : .template)
|
||||
})
|
||||
.disabled(isCameraPublishingBusy)
|
||||
|
||||
|
||||
Button(action: {
|
||||
Task {
|
||||
isMicrophonePublishingBusy = true
|
||||
defer { Task { @MainActor in isMicrophonePublishingBusy = false } }
|
||||
do {
|
||||
try await room.localParticipant.setMicrophone(enabled: !isMicrophoneEnabled)
|
||||
} catch {
|
||||
print("Failed to toggle microphone: \(error)")
|
||||
}
|
||||
}
|
||||
},
|
||||
label: {
|
||||
Image(systemSymbol: isMicrophoneEnabled ? .micFill : .micSlashFill)
|
||||
.renderingMode(isMicrophoneEnabled ? .original : .template)
|
||||
})
|
||||
.disabled(isMicrophonePublishingBusy)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ParticipantLayout<Content: View>: View {
|
||||
let views: [AnyView]
|
||||
let spacing: CGFloat
|
||||
|
||||
init<Data: RandomAccessCollection>(
|
||||
_ data: Data,
|
||||
id: KeyPath<Data.Element, Data.Element> = \.self,
|
||||
spacing: CGFloat,
|
||||
@ViewBuilder content: @escaping (Data.Element) -> Content
|
||||
) {
|
||||
self.spacing = spacing
|
||||
views = data.map { AnyView(content($0[keyPath: id])) }
|
||||
}
|
||||
|
||||
func computeColumn(with geometry: GeometryProxy) -> (x: Int, y: Int) {
|
||||
let sqr = Double(views.count).squareRoot()
|
||||
let r: [Int] = [Int(sqr.rounded()), Int(sqr.rounded(.up))]
|
||||
let c = geometry.isTall ? r : r.reversed()
|
||||
return (x: c[0], y: c[1])
|
||||
}
|
||||
|
||||
func grid(axis: Axis, geometry: GeometryProxy) -> some View {
|
||||
ScrollView([axis == .vertical ? .vertical : .horizontal]) {
|
||||
HorVGrid(axis: axis, columns: [GridItem(.flexible())], spacing: spacing) {
|
||||
ForEach(0 ..< views.count, id: \.self) { i in
|
||||
views[i]
|
||||
.aspectRatio(1, contentMode: .fill)
|
||||
}
|
||||
}
|
||||
.padding(axis == .horizontal ? [.leading, .trailing] : [.top, .bottom],
|
||||
max(0, ((axis == .horizontal ? geometry.size.width : geometry.size.height)
|
||||
- ((axis == .horizontal ? geometry.size.height : geometry.size.width) * CGFloat(views.count)) - (spacing * CGFloat(views.count - 1))) / 2))
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
if views.isEmpty {
|
||||
EmptyView()
|
||||
} else if geometry.size.width <= 300 {
|
||||
grid(axis: .vertical, geometry: geometry)
|
||||
} else if geometry.size.height <= 300 {
|
||||
grid(axis: .horizontal, geometry: geometry)
|
||||
} else {
|
||||
let verticalWhenTall: Axis = geometry.isTall ? .vertical : .horizontal
|
||||
let horizontalWhenTall: Axis = geometry.isTall ? .horizontal : .vertical
|
||||
|
||||
switch views.count {
|
||||
// simply return first view
|
||||
case 1: views[0]
|
||||
case 3: HorVStack(axis: verticalWhenTall, spacing: spacing) {
|
||||
views[0]
|
||||
HorVStack(axis: horizontalWhenTall, spacing: spacing) {
|
||||
views[1]
|
||||
views[2]
|
||||
}
|
||||
}
|
||||
case 5: HorVStack(axis: verticalWhenTall, spacing: spacing) {
|
||||
views[0]
|
||||
if geometry.isTall {
|
||||
HStack(spacing: spacing) {
|
||||
views[1]
|
||||
views[2]
|
||||
}
|
||||
HStack(spacing: spacing) {
|
||||
views[3]
|
||||
views[4]
|
||||
}
|
||||
} else {
|
||||
VStack(spacing: spacing) {
|
||||
views[1]
|
||||
views[3]
|
||||
}
|
||||
VStack(spacing: spacing) {
|
||||
views[2]
|
||||
views[4]
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
let c = computeColumn(with: geometry)
|
||||
VStack(spacing: spacing) {
|
||||
ForEach(0 ... (c.y - 1), id: \.self) { y in
|
||||
HStack(spacing: spacing) {
|
||||
ForEach(0 ... (c.x - 1), id: \.self) { x in
|
||||
let index = (y * c.x) + x
|
||||
if index < views.count {
|
||||
views[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HorVStack<Content: View>: View {
|
||||
let axis: Axis
|
||||
let horizontalAlignment: HorizontalAlignment
|
||||
let verticalAlignment: VerticalAlignment
|
||||
let spacing: CGFloat?
|
||||
let content: () -> Content
|
||||
|
||||
init(axis: Axis = .horizontal,
|
||||
horizontalAlignment: HorizontalAlignment = .center,
|
||||
verticalAlignment: VerticalAlignment = .center,
|
||||
spacing: CGFloat? = nil,
|
||||
@ViewBuilder content: @escaping () -> Content)
|
||||
{
|
||||
self.axis = axis
|
||||
self.horizontalAlignment = horizontalAlignment
|
||||
self.verticalAlignment = verticalAlignment
|
||||
self.spacing = spacing
|
||||
self.content = content
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if axis == .vertical {
|
||||
VStack(alignment: horizontalAlignment, spacing: spacing, content: content)
|
||||
} else {
|
||||
HStack(alignment: verticalAlignment, spacing: spacing, content: content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HorVGrid<Content: View>: View {
|
||||
let axis: Axis
|
||||
let spacing: CGFloat?
|
||||
let content: () -> Content
|
||||
let columns: [GridItem]
|
||||
|
||||
init(axis: Axis = .horizontal,
|
||||
columns: [GridItem],
|
||||
spacing: CGFloat? = nil,
|
||||
@ViewBuilder content: @escaping () -> Content)
|
||||
{
|
||||
self.axis = axis
|
||||
self.spacing = spacing
|
||||
self.columns = columns
|
||||
self.content = content
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if axis == .vertical {
|
||||
LazyVGrid(columns: columns, spacing: spacing, content: content)
|
||||
} else {
|
||||
LazyHGrid(rows: columns, spacing: spacing, content: content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension GeometryProxy {
|
||||
public var isTall: Bool {
|
||||
size.height > size.width
|
||||
}
|
||||
|
||||
var isWide: Bool {
|
||||
size.width > size.height
|
||||
}
|
||||
}
|
||||
77
application-client/openvidu-ios/iOS/Info.plist
Normal file
@ -0,0 +1,77 @@
|
||||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.video</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>$(DISPLAY_NAME)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>livekit</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>uses your camera for video chat</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Keychain is used to store all preferences.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>uses your microphone for video chat</string>
|
||||
<key>RTCAppGroupIdentifier</key>
|
||||
<string>group.io.lopenvidu.ios</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>voip</string>
|
||||
</array>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
20
application-client/openvidu-ios/iOS/iOS.entitlements
Normal file
@ -0,0 +1,20 @@
|
||||
<?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>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)keychain-group.io.lopenvidu.ios</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||