Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Coverage Badge

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
coverage:
name: Update Coverage Badge
runs-on: macos-15
environment: "Coverage Badge Env" # Use specified GitHub environment

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Cache Gems
uses: actions/cache@v4
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-

- name: Bundle Install
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3

- name: Run Tests with Coverage
env:
SWIFT_VERSION: "6.0"
run: bundle exec fastlane sdk_tests_with_coverage

- name: Extract Coverage
id: coverage
run: |
ruby script/extract_coverage.rb

- name: Update Coverage Badge
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: schneegans/[email protected]
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: ${{ secrets.GIST_ID }}
filename: line-sdk-ios-coverage.json
label: Coverage
message: ${{ steps.coverage.outputs.coverage }}
color: ${{ steps.coverage.outputs.color }}

- name: Deploy Coverage Report to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./coverage_output
publish_branch: gh-pages
enable_jekyll: false
allow_empty_commit: false
force_orphan: true
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'
commit_message: 'Deploy coverage report for ${{ github.sha }}'

- name: Upload Coverage Artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-reports
path: |
./coverage_output/
./test_output/
retention-days: 30
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ gem "fastlane"
gem "jazzy"
gem "xcode-install"
gem "cocoapods"
gem "xcov"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
10 changes: 10 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ GEM
simctl (1.6.10)
CFPropertyList
naturally
slack-notifier (2.4.0)
sqlite3 (1.7.3-arm64-darwin)
sysrandom (1.0.5)
terminal-notifier (2.0.0)
Expand Down Expand Up @@ -310,10 +311,18 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcov (1.8.1)
fastlane (>= 2.141.0, < 3.0.0)
multipart-post
slack-notifier
terminal-table
xcodeproj
xcresult (~> 0.2.0)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
xcresult (0.2.2)

PLATFORMS
arm64-darwin-22
Expand All @@ -326,6 +335,7 @@ DEPENDENCIES
fastlane-plugin-create_xcframework
jazzy
xcode-install
xcov

BUNDLED WITH
2.3.26
14 changes: 14 additions & 0 deletions LineSDK/LineSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
2B8D25332E39A2540029FB34 /* LoginFlowFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B8D25322E39A2540029FB34 /* LoginFlowFactory.swift */; };
2B8D25342E39A2540029FB34 /* LoginFlowFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B8D25322E39A2540029FB34 /* LoginFlowFactory.swift */; };
2B8D25362E39A2B50029FB34 /* LoginProcessMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B8D25352E39A2B50029FB34 /* LoginProcessMocks.swift */; };
2B8D25382E39A2D40029FB34 /* LoginProcessFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B8D25372E39A2D40029FB34 /* LoginProcessFlowTests.swift */; };
3F75522F2123D215004AC047 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F75522E2123D214004AC047 /* Assets.xcassets */; };
3F75523121244502004AC047 /* LoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F75523021244502004AC047 /* LoginButton.swift */; };
3F946A212126D13A009914ED /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = 3F946A202126D13A009914ED /* [email protected] */; };
Expand Down Expand Up @@ -576,6 +580,9 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
2B8D25322E39A2540029FB34 /* LoginFlowFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFlowFactory.swift; sourceTree = "<group>"; };
2B8D25352E39A2B50029FB34 /* LoginProcessMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginProcessMocks.swift; sourceTree = "<group>"; };
2B8D25372E39A2D40029FB34 /* LoginProcessFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginProcessFlowTests.swift; sourceTree = "<group>"; };
3F75522E2123D214004AC047 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3F75523021244502004AC047 /* LoginButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginButton.swift; sourceTree = "<group>"; };
3F946A202126D13A009914ED /* [email protected] */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "[email protected]"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1194,6 +1201,8 @@
4B8A9655211009A400760219 /* Login */ = {
isa = PBXGroup;
children = (
2B8D25352E39A2B50029FB34 /* LoginProcessMocks.swift */,
2B8D25372E39A2D40029FB34 /* LoginProcessFlowTests.swift */,
4B8FF4922105C49200890AEF /* LoginFlowTests.swift */,
4B8A965621100A5800760219 /* LoginConfigurationTests.swift */,
4B792FB021102D9200EDDD1E /* LoginProcessURLResponseTests.swift */,
Expand Down Expand Up @@ -1291,6 +1300,7 @@
4B6508B7211812CE001796E0 /* LoginManagerOptions.swift */,
4BD33EE2238B825600C1E8A9 /* LoginManagerParameters.swift */,
4B9058DB21007C8C004D717F /* LoginResult.swift */,
2B8D25322E39A2540029FB34 /* LoginFlowFactory.swift */,
4B45256A2101810D00A39D4F /* LoginProcess.swift */,
4B63F4722106FDCC003D1BF1 /* LoginProcessURLResponse.swift */,
4B45256E210188C300A39D4F /* LoginPermission.swift */,
Expand Down Expand Up @@ -2371,6 +2381,7 @@
D1F20A762500BBF4005E359E /* OpenChatCreatingFormItem.swift in Sources */,
4B45256B2101810D00A39D4F /* LoginProcess.swift in Sources */,
4B9A303121210FE700174C6F /* ImageMessage.swift in Sources */,
2B8D25332E39A2540029FB34 /* LoginFlowFactory.swift in Sources */,
DBF20278212C137D00780358 /* GetApproversInGroupRequest.swift in Sources */,
4BFBEA7B211057050044C2B6 /* PostRefreshTokenRequest.swift in Sources */,
);
Expand Down Expand Up @@ -2409,6 +2420,7 @@
4BA8E44A210ED82B00355F03 /* AdapterTests.swift in Sources */,
DB75650928CB254A001A25A5 /* UIColorExtensionTests.swift in Sources */,
4B6A0FF7212AACCF00B3ED1F /* FlexButtonComponentTests.swift in Sources */,
2B8D25382E39A2D40029FB34 /* LoginProcessFlowTests.swift in Sources */,
DB09851523D5AF9D0001A3B8 /* PKCETests.swift in Sources */,
4BBAFC502101D31300E7BFF6 /* ConstantTests.swift in Sources */,
4B9A305621215DED00174C6F /* TemplateConfirmPayloadTests.swift in Sources */,
Expand All @@ -2435,6 +2447,7 @@
4BEFF644226DCD960046DB66 /* ShareControllerTests.swift in Sources */,
4BFC09EF213CCE7700F4594D /* JWKTests.swift in Sources */,
4B3CCB9E2152449400F51D76 /* ECDSAKeyTests.swift in Sources */,
2B8D25362E39A2B50029FB34 /* LoginProcessMocks.swift in Sources */,
4B414D5E210F077700FD19BC /* RequestStubs.swift in Sources */,
4B5EE2E8212BE0D00009DF2E /* FlexCarouselContainerTests.swift in Sources */,
4BCD27952113ECCF00B90D8F /* GetVerifyTokenRequestTests.swift in Sources */,
Expand Down Expand Up @@ -2703,6 +2716,7 @@
4BCD252B23FFAABA00D4B6BD /* AccessTokenVerifyResult.swift in Sources */,
4BCD253F23FFAABA00D4B6BD /* PostMultisendMessagesWithTokenRequest.swift in Sources */,
4BCD24F123FFA74100D4B6BD /* LineSDKAudioMessage.swift in Sources */,
2B8D25342E39A2540029FB34 /* LoginFlowFactory.swift in Sources */,
4BCD256D23FFAABA00D4B6BD /* ResponsePipeline.swift in Sources */,
4BCD255A23FFAABA00D4B6BD /* FlexButtonComponent.swift in Sources */,
4BCD24F223FFA74100D4B6BD /* LineSDKFlexMessageContainer.swift in Sources */,
Expand Down
109 changes: 109 additions & 0 deletions LineSDK/LineSDK/Login/LoginFlowFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// LoginFlowFactory.swift
//
// Copyright (c) 2016-present, LY Corporation. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by LY Corporation.
//
// As with any software that integrates with the LY Corporation platform, your use of this software
// is subject to the LINE Developers Agreement [http://terms2.line.me/LINE_Developers_Agreement].
// This copyright notice shall be included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import UIKit
import Foundation

// MARK: - Application Opener Protocol

/// Protocol to abstract UIApplication.open behavior for testing
@MainActor protocol ApplicationOpener {
func open(
_ url: URL,
options: [UIApplication.OpenExternalURLOptionsKey : Any],
completionHandler completion: (@MainActor @Sendable (Bool) -> Void)?
)
}

extension UIApplication: ApplicationOpener { }

// MARK: - Flow Protocols

/// Protocol for AppUniversalLinkFlow behavior
@MainActor
protocol AppUniversalLinkFlowType: AnyObject {
var onNext: Delegate<Bool, Void> { get }
func start()
}

/// Protocol for AppAuthSchemeFlow behavior
@MainActor
protocol AppAuthSchemeFlowType: AnyObject {
var onNext: Delegate<Bool, Void> { get }
func start()
}

/// Protocol for WebLoginFlow behavior
@MainActor
protocol WebLoginFlowType: AnyObject {
var onNext: Delegate<WebLoginFlow.Next, Void> { get }
var onCancel: Delegate<(), Void> { get }
func start(in viewController: UIViewController?)
func dismiss()
}

// MARK: - LINE Availability Checker Protocol

/// Protocol to check if LINE app is available
protocol LINEAvailabilityChecker: Sendable {
@MainActor var isLINEInstalled: Bool { get }
}

/// Default implementation using Constant.isLINEInstalled
struct DefaultLINEAvailabilityChecker: LINEAvailabilityChecker {
@MainActor var isLINEInstalled: Bool {
return Constant.isLINEInstalled
}
}

// MARK: - Flow Factory Protocol

/// Factory protocol for creating login flows (allows dependency injection)
@MainActor
protocol LoginFlowFactory: Sendable {
func createAppUniversalLinkFlow(parameter: LoginProcess.FlowParameters) -> AppUniversalLinkFlowType
func createAppAuthSchemeFlow(parameter: LoginProcess.FlowParameters) -> AppAuthSchemeFlowType
func createWebLoginFlow(parameter: LoginProcess.FlowParameters) -> WebLoginFlowType
}

// MARK: - Default Implementation

/// Default factory implementation using real flows
@MainActor
class DefaultLoginFlowFactory: LoginFlowFactory {
private let applicationOpener: ApplicationOpener

init(applicationOpener: ApplicationOpener = UIApplication.shared) {
self.applicationOpener = applicationOpener
}

func createAppUniversalLinkFlow(parameter: LoginProcess.FlowParameters) -> AppUniversalLinkFlowType {
return AppUniversalLinkFlow(parameter: parameter, applicationOpener: applicationOpener)
}

func createAppAuthSchemeFlow(parameter: LoginProcess.FlowParameters) -> AppAuthSchemeFlowType {
return AppAuthSchemeFlow(parameter: parameter, applicationOpener: applicationOpener)
}

func createWebLoginFlow(parameter: LoginProcess.FlowParameters) -> WebLoginFlowType {
return WebLoginFlow(parameter: parameter)
}
}
Loading
Loading