├── TransNotion ├── Debug.xcconfig ├── Release.xcconfig ├── Assets.xcassets │ ├── Contents.json │ ├── notion_icon.imageset │ │ ├── notion_icon.png │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── en.lproj │ └── Localizable.strings ├── Service │ ├── Database │ │ ├── Remote │ │ │ ├── DatabaseEntity.swift │ │ │ ├── DatabasePathBuilder.swift │ │ │ ├── CollectionReference+Combine.swift │ │ │ ├── DocumentReference+Combine.swift │ │ │ └── Database.swift │ │ └── Local │ │ │ └── LocalStore.swift │ ├── Logger │ │ └── ErrorLogger.swift │ └── Auth │ │ ├── Auth.swift │ │ └── OAuth.swift ├── ja.lproj │ └── Localizable.strings ├── Entity │ ├── Me.swift │ ├── Credential.swift │ └── User.swift ├── Modifier │ └── Placeholder.swift ├── Secret.swift.sample ├── ContentView.swift ├── Extension │ ├── Notion.Object.Page+Extension.swift │ └── JSONCoder+Extension.swift ├── Style │ ├── Toggle.swift │ ├── Button.swift │ └── Color+Extension.swift ├── Environment │ ├── Environment+notion.swift │ └── Environment+Window.swift ├── RootView.swift ├── Domain │ ├── NotionWebView │ │ ├── NotionWebViewPage.swift │ │ └── NotionWebView.swift │ ├── Login │ │ └── LoginView.swift │ ├── NotionPages │ │ └── NotionPagesView.swift │ └── NotionPage │ │ └── NotionPage.swift ├── AppDelegate.swift ├── TransNotionApp.swift └── Info.plist ├── .gitignore ├── TransNotion.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved ├── xcshareddata │ └── xcschemes │ │ └── TransNotion.xcscheme └── project.pbxproj ├── Makefile ├── TransNotionTests ├── Info.plist └── TransNotionTests.swift ├── TransNotionUITests ├── Info.plist └── TransNotionUITests.swift └── scripts └── secret.sh /TransNotion/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Debug-Secret.xcconfig" 2 | -------------------------------------------------------------------------------- /TransNotion/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Release-Secret.xcconfig" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Secret.swift 3 | GoogleService-Info.plist 4 | GoogleService*.plist 5 | *-Secret.xcconfig 6 | -------------------------------------------------------------------------------- /TransNotion/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /TransNotion/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /TransNotion/Assets.xcassets/notion_icon.imageset/notion_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bannzai/TransNotion-Swift/HEAD/TransNotion/Assets.xcassets/notion_icon.imageset/notion_icon.png -------------------------------------------------------------------------------- /TransNotion/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | TransNotion 4 | 5 | Created by Yudai.Hirose on 2021/05/15. 6 | 7 | */ 8 | 9 | "Login with Notion" = "Login with Notion"; 10 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Remote/DatabaseEntity.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol DatabaseEntity: Codable { 4 | associatedtype WhereKey: CodingKey & RawRepresentable where WhereKey.RawValue == String 5 | } 6 | -------------------------------------------------------------------------------- /TransNotion.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TransNotion/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TransNotion/ja.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | TransNotion 4 | 5 | Created by Yudai.Hirose on 2021/05/15. 6 | 7 | */ 8 | 9 | "Login with Notion" = "Notionでログイン"; 10 | "Translate this page" = "このページを翻訳する"; 11 | -------------------------------------------------------------------------------- /TransNotion/Entity/Me.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Me: Identifiable, Equatable { 4 | let id: ID 5 | var userID: UserID { UserID(rawValue: id.rawValue) } 6 | 7 | struct ID: RawRepresentable, Equatable, Hashable { 8 | let rawValue: String 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TransNotion/Modifier/Placeholder.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | extension View { 4 | @ViewBuilder func placeholder(when showPlaceholder: Bool) -> some View { 5 | if showPlaceholder { 6 | redacted(reason: .placeholder) 7 | } else { 8 | unredacted() 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TransNotion.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .PHONY: secret 5 | secret: 6 | echo $(DEVELOPMENT_GOOGLE_SERVICE_INFO_PLIST) | base64 -D > TransNotion/GoogleService-Info-dev.plist 7 | echo $(DEBUG_SECRET_XCCONFIG) | base64 -D > TransNotion/Debug-Secret.xcconfig 8 | echo $(RELEASE_SECRET_XCCONFIG) | base64 -D > TransNotion/Release-Secret.xcconfig 9 | ./scripts/secret.sh 10 | -------------------------------------------------------------------------------- /TransNotion/Secret.swift.sample: -------------------------------------------------------------------------------- 1 | 2 | 3 | enum Secret { 4 | static let notionOAuthClientID = "NOTION_OAUTH_CLIENT_ID" 5 | static let notionOAuthRedirectURI = "NOTION_OAUTH_REDIRECT_URI" 6 | static let notionOAuthCallbackCustomURLScheme = "NOTION_OAUTH_CALLBACK_CUSTOM_URL_SCHEME" 7 | static let notionOAuthClientSecret = "NOTION_OAUTH_CLIENT_SECRET" 8 | } 9 | -------------------------------------------------------------------------------- /TransNotion/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | NotionPagesView() 13 | } 14 | } 15 | 16 | struct ContentView_Previews: PreviewProvider { 17 | static var previews: some View { 18 | ContentView() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TransNotion/Assets.xcassets/notion_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "notion_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TransNotion/Extension/Notion.Object.Page+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notion.Object.Page+Extension.swift 3 | // TransNotion 4 | // 5 | // Created by 廣瀬雄大 on 2021/06/22. 6 | // 7 | 8 | import Foundation 9 | import notion 10 | 11 | extension Object.Page { 12 | private var publishedPageID: String { 13 | id.replacingOccurrences(of: "-", with: "") 14 | } 15 | 16 | func pageURL() -> URL? { 17 | .init(string: "https://www.notion.so/\(publishedPageID)") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TransNotion/Entity/Credential.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Credential.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Credential: Codable { 11 | let accessToken: String 12 | let workspaceName: String 13 | // NOTE: workspaceIcon is exists into document. But exactly not contains in response 14 | let workspaceIcon: String? 15 | let botId: String 16 | } 17 | 18 | struct Credentials: Codable, LocalStoreKey { 19 | var elements: [Credential] 20 | } 21 | -------------------------------------------------------------------------------- /TransNotion/Service/Logger/ErrorLogger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorLogger.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import Foundation 9 | import FirebaseCrashlytics 10 | 11 | protocol ErrorLogger { 12 | func record(error: Swift.Error) 13 | } 14 | private struct _ErrorLogger: ErrorLogger { 15 | func record(error: Error) { 16 | print("error: \(error)") 17 | Crashlytics.crashlytics().record(error: error) 18 | } 19 | } 20 | 21 | let errorLogger: ErrorLogger = _ErrorLogger() 22 | -------------------------------------------------------------------------------- /TransNotion/Style/Toggle.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CheckBoxToggleStyle: ToggleStyle { 4 | func makeBody(configuration: Configuration) -> some View { 5 | return HStack { 6 | configuration.label 7 | Spacer() 8 | Image(systemName: configuration.isOn ? "checkmark.square" : "square") 9 | .resizable() 10 | .frame(width: 20, height: 20) 11 | .padding() 12 | .onTapGesture { configuration.isOn.toggle() } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TransNotion/Style/Button.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct PrimaryButtonStyle: ButtonStyle { 4 | @Environment(\.isEnabled) var isEnabled 5 | let width: CGFloat 6 | 7 | func makeBody(configuration: Configuration) -> some View { 8 | configuration.label 9 | .opacity(configuration.isPressed ? 0.2 : 1.0) 10 | .frame(maxWidth: width) 11 | .padding(.vertical, 14) 12 | .background(isEnabled ? Color.appPrimary : Color.appPrimary.opacity(0.4)) 13 | .foregroundColor(.white) 14 | .cornerRadius(4) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TransNotion/Environment/Environment+notion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Environment+notion.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/17. 6 | // 7 | 8 | import Foundation 9 | import notion 10 | import SwiftUI 11 | 12 | struct NotionAPIClientKey: EnvironmentKey { 13 | static let defaultValue: notion.Session = .shared 14 | } 15 | 16 | extension EnvironmentValues { 17 | var notion: notion.Session { 18 | get { 19 | self[NotionAPIClientKey.self] 20 | } 21 | set { 22 | self[NotionAPIClientKey.self] = newValue 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TransNotion/Entity/User.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import FirebaseFirestore 3 | import FirebaseFirestoreSwift 4 | 5 | struct UserID: RawRepresentable, Codable, DocumentIDWrappable, Hashable { 6 | let rawValue: String 7 | 8 | static func wrap(_ documentReference: DocumentReference) throws -> UserID { 9 | UserID(rawValue: documentReference.documentID) 10 | } 11 | } 12 | 13 | struct User: DatabaseEntity, Equatable, Identifiable { 14 | @DocumentID var id: UserID? 15 | let anonymousUserID: UserID 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case anonymousUserID 19 | } 20 | typealias WhereKey = CodingKeys 21 | } 22 | -------------------------------------------------------------------------------- /TransNotion/Extension/JSONCoder+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONCoder+Extension.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import Foundation 9 | 10 | extension JSONDecoder { 11 | static let convertFromSnakeCase: JSONDecoder = { 12 | let decoder = JSONDecoder() 13 | decoder.keyDecodingStrategy = .convertFromSnakeCase 14 | return decoder 15 | }() 16 | } 17 | extension JSONEncoder { 18 | static let convertToSnakeCase: JSONEncoder = { 19 | let encoder = JSONEncoder() 20 | encoder.keyEncodingStrategy = .convertToSnakeCase 21 | return encoder 22 | }() 23 | } 24 | -------------------------------------------------------------------------------- /TransNotion/Style/Color+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color+Extension.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension SwiftUI.Color { 11 | // #4c5870 12 | static let appPrimary: Color = Color(red: 76 / 255, green: 88 / 255, blue: 112 / 255) 13 | 14 | // #3d3d3b 15 | static let appGray: Color = Color(red: 61 / 255, green: 61 / 255, blue: 59 / 255) 16 | 17 | // #a2a9af 18 | static let background: Color = Color(red: 162 / 255, green: 162 / 255, blue: 175 / 255) 19 | 20 | // #ebedec 21 | static let textColor: Color = Color(red: 235 / 255, green: 237 / 255, blue: 236 / 255) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Remote/DatabasePathBuilder.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import FirebaseFirestore 3 | 4 | struct DatabaseDocumentPathBuilder { 5 | let path: String 6 | } 7 | 8 | struct DatabaseCollectionPathBuilder { 9 | let path: String 10 | var args: (key: Entity.WhereKey, relations: [CollectionRelation])? = nil 11 | var isGroup: Bool = false 12 | 13 | static func users() -> DatabaseCollectionPathBuilder { .init(path: "/users") } 14 | } 15 | 16 | enum CollectionRelation { 17 | case equal(Any) 18 | case less(Any) 19 | case lessOrEqual(Any) 20 | case greater(Any) 21 | case greaterOrEqual(Any) 22 | case notEqual(Any) 23 | case contains([Any]) 24 | } 25 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Remote/CollectionReference+Combine.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Combine 3 | import FirebaseFirestore 4 | import FirebaseFirestoreSwift 5 | 6 | extension CollectionReference { 7 | // MARK: - Add 8 | func add(value: T) -> AnyPublisher { 9 | return Future { [weak self] promise in 10 | do { 11 | _ = try self?.addDocument(from: value, encoder: Firestore.Encoder()) { error in 12 | if let error = error { 13 | promise(.failure(error)) 14 | } 15 | promise(.success(value)) 16 | } 17 | } catch { 18 | promise(.failure(error)) 19 | } 20 | }.eraseToAnyPublisher() 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /TransNotionTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TransNotionUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /scripts/secret.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | set -eu 3 | set -o pipefail 4 | 5 | SECRET_FILE_PATH=`dirname $0`/../TransNotion 6 | cd $SECRET_FILE_PATH 7 | 8 | echo $(pwd -P) 9 | 10 | cp -i Secret.swift.sample Secret.swift 11 | 12 | echo 'Start to replace from NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_REDIRECT_URI, NOTION_OAUTH_CALLBACK_CUSTOM_URL_SCHEME, NOTION_OAUTH_CLIENT_SECRET' 13 | 14 | sed -i '' -e "s;NOTION_OAUTH_CLIENT_ID;$NOTION_OAUTH_CLIENT_ID;" Secret.swift 15 | sed -i '' -e "s;NOTION_OAUTH_REDIRECT_URI;$NOTION_OAUTH_REDIRECT_URI;" Secret.swift 16 | sed -i '' -e "s;NOTION_OAUTH_CALLBACK_CUSTOM_URL_SCHEME;$NOTION_OAUTH_CALLBACK_CUSTOM_URL_SCHEME;" Secret.swift 17 | sed -i '' -e "s;NOTION_OAUTH_CLIENT_SECRET;$NOTION_OAUTH_CLIENT_SECRET;" Secret.swift 18 | 19 | echo 'Done to replace from NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_REDIRECT_URI, NOTION_OAUTH_CALLBACK_CUSTOM_URL_SCHEME, NOTION_OAUTH_CLIENT_SECRET' 20 | -------------------------------------------------------------------------------- /TransNotion/RootView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootView.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import SwiftUI 9 | import notion 10 | 11 | struct RootView: View { 12 | @EnvironmentObject var state: TransNotionApp.State 13 | var body: some View { 14 | if state.isLogin, let credential = state.credential { 15 | ContentView() 16 | .environment(\.notion, { 17 | let session = notion.Session.shared 18 | session.setAuthorization(token: credential.accessToken) 19 | return session 20 | }()) 21 | } else { 22 | LoginView() 23 | .environmentObject(state) 24 | } 25 | } 26 | } 27 | 28 | struct RootView_Previews: PreviewProvider { 29 | static var previews: some View { 30 | RootView() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TransNotion/Environment/Environment+Window.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Widnow.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | typealias KeyWindowClosure = () -> UIWindow? 12 | 13 | struct KeyWindowKey: EnvironmentKey { 14 | static let defaultValue: KeyWindowClosure = { 15 | UIApplication 16 | .shared 17 | .connectedScenes 18 | .filter({ $0.activationState == .foregroundActive }) 19 | .map({ $0 as? UIWindowScene }) 20 | .compactMap({ $0 }) 21 | .flatMap(\.windows) 22 | .first(where: \.isKeyWindow) 23 | } 24 | } 25 | 26 | extension EnvironmentValues { 27 | var keyWindow: KeyWindowClosure { 28 | get { 29 | self[KeyWindowKey.self] 30 | } 31 | set { 32 | self[KeyWindowKey.self] = newValue 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /TransNotion/Domain/NotionWebView/NotionWebViewPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotionWebViewPage.swift 3 | // TransNotion 4 | // 5 | // Created by 廣瀬雄大 on 2021/06/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NotionWebViewPage: View { 11 | @State var url: URL 12 | @State var isLoading = false 13 | 14 | var body: some View { 15 | VStack { 16 | NotionWebView(url: $url, isLoading: $isLoading) 17 | 18 | Spacer() 19 | 20 | Button("Translate this page") { 21 | print("TODO: Translate and extract currnet page") 22 | print("URL: \(url)", "isLoading: \(isLoading)") 23 | } 24 | .buttonStyle(PrimaryButtonStyle(width: .infinity)) 25 | .padding(.horizontal, 20) 26 | .disabled(isLoading) 27 | .placeholder(when: isLoading) 28 | 29 | Spacer().frame(height: 40) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TransNotion/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import UIKit 9 | import Firebase 10 | 11 | let isPreview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" 12 | 13 | final class AppDelegate: NSObject, UIApplicationDelegate { 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | if !isPreview { 16 | setupFirebase() 17 | } 18 | 19 | return true 20 | } 21 | } 22 | 23 | private func setupFirebase() { 24 | #if DEBUG 25 | FirebaseApp.configure(options: FirebaseOptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info-dev", ofType: "plist")!)!) 26 | #else 27 | FirebaseApp.configure(options: FirebaseOptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info-prod", ofType: "plist")!)!) 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /TransNotionTests/TransNotionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransNotionTests.swift 3 | // TransNotionTests 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import XCTest 9 | @testable import TransNotion 10 | 11 | class TransNotionTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Local/LocalStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalStore.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol LocalStoreKey { 11 | static var localStoreKey: String { get } 12 | } 13 | 14 | extension LocalStoreKey { 15 | static var localStoreKey: String { "local_store_\(type(of: self))" } 16 | } 17 | 18 | struct LocalStore { 19 | func isExists() -> Bool { 20 | UserDefaults.standard.dictionaryRepresentation().keys.contains(where: { $0 == Coder.localStoreKey }) 21 | } 22 | 23 | func write(for coder: Coder) throws { 24 | let data = try JSONEncoder().encode(coder) 25 | UserDefaults.standard.set(data, forKey: Coder.localStoreKey) 26 | } 27 | 28 | func read() throws -> Coder? { 29 | guard let data = UserDefaults.standard.data(forKey: Coder.localStoreKey) else { 30 | return nil 31 | } 32 | let decoded = try JSONDecoder().decode(Coder.self, from: data) 33 | return decoded 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /TransNotion/TransNotionApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransNotionApp.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct TransNotionApp: App { 12 | @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate 13 | 14 | var body: some Scene { 15 | WindowGroup { 16 | RootView() 17 | .environmentObject(State()) 18 | } 19 | } 20 | } 21 | 22 | extension TransNotionApp { 23 | final class State: ObservableObject { 24 | @Published var isLogin: Bool = { 25 | let store = LocalStore() 26 | do { 27 | let credentials = try store.read() 28 | return credentials?.elements.last != nil 29 | } catch { 30 | print(error) 31 | return false 32 | } 33 | }() 34 | var credential: Credential? { 35 | let store = LocalStore() 36 | do { 37 | let credentials = try store.read() 38 | return credentials?.elements.last 39 | } catch { 40 | errorLogger.record(error: error) 41 | return nil 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TransNotionUITests/TransNotionUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransNotionUITests.swift 3 | // TransNotionUITests 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import XCTest 9 | 10 | class TransNotionUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TransNotion/Service/Auth/Auth.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import FirebaseAuth 3 | import Combine 4 | 5 | protocol Auth { 6 | func auth() -> AnyPublisher 7 | } 8 | 9 | protocol Authentificated { 10 | func authentificated() -> Me 11 | } 12 | 13 | fileprivate class _Auth: Auth, Authentificated { 14 | var me: Me? 15 | init() { } 16 | 17 | func auth() -> AnyPublisher { 18 | Future { promise in 19 | FirebaseAuth.Auth.auth().signInAnonymously() { (result, error) in 20 | if let error = error { 21 | promise(.failure(error)) 22 | return 23 | } 24 | guard let result = result else { 25 | fatalError("unexpected pattern about result and error is nil") 26 | } 27 | let id = Me.ID(rawValue: result.user.uid) 28 | self.store(meID: id) 29 | 30 | let me = Me(id: id) 31 | self.me = me 32 | promise(.success(me)) 33 | } 34 | }.eraseToAnyPublisher() 35 | } 36 | 37 | func authentificated() -> Me { 38 | me! 39 | } 40 | 41 | // MARK: - Private 42 | private enum StoreKey { 43 | static let firebaseUserID: String = "firebaseUserID" 44 | } 45 | private func store(meID: Me.ID) { 46 | guard UserDefaults.standard.string(forKey: StoreKey.firebaseUserID) == nil else { 47 | return 48 | } 49 | UserDefaults.standard.setValue(meID.rawValue, forKey: StoreKey.firebaseUserID) 50 | } 51 | } 52 | 53 | private var _auth = _Auth() 54 | internal var auth: Auth { _auth } 55 | internal var authentificated: Authentificated { _auth } 56 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Remote/DocumentReference+Combine.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Combine 3 | import FirebaseFirestore 4 | import FirebaseFirestoreSwift 5 | 6 | extension DocumentReference { 7 | // MARK: - Get 8 | enum GetDocumentError: Error { 9 | case snapshotIsNotFound 10 | case snapshotDecodeFailure 11 | } 12 | func get() -> AnyPublisher { 13 | Future { [weak self] promise in 14 | self?.getDocument(source: .default, completion: { (snapshot, error) in 15 | if let error = error { 16 | promise(.failure(error)) 17 | } 18 | guard let snapshot = snapshot else { 19 | return promise(.failure(GetDocumentError.snapshotIsNotFound)) 20 | } 21 | 22 | do { 23 | guard let decoded = try snapshot.data(as: T.self) else { 24 | return promise(.failure(GetDocumentError.snapshotDecodeFailure)) 25 | } 26 | promise(.success(decoded)) 27 | } catch { 28 | promise(.failure(error)) 29 | } 30 | }) 31 | }.eraseToAnyPublisher() 32 | } 33 | 34 | 35 | // MARK: - Set 36 | func set(from value: T) -> AnyPublisher { 37 | Future { [weak self] promise in 38 | do { 39 | try self?.setData(from: value, merge: true, encoder: Firestore.Encoder()) { error in 40 | guard let error = error else { 41 | promise(.success(value)) 42 | return 43 | } 44 | promise(.failure(error)) 45 | } 46 | } catch { 47 | promise(.failure(error)) 48 | } 49 | }.eraseToAnyPublisher() 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /TransNotion/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TransNotion/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleURLTypes 6 | 7 | 8 | CFBundleTypeRole 9 | Editor 10 | CFBundleURLSchemes 11 | 12 | $(NOTION_OATUH_CALLBACK_CUSTOM_URL_SCHEME) 13 | 14 | 15 | 16 | CFBundleDevelopmentRegion 17 | $(DEVELOPMENT_LANGUAGE) 18 | CFBundleExecutable 19 | $(EXECUTABLE_NAME) 20 | CFBundleIdentifier 21 | $(PRODUCT_BUNDLE_IDENTIFIER) 22 | CFBundleInfoDictionaryVersion 23 | 6.0 24 | CFBundleName 25 | $(PRODUCT_NAME) 26 | CFBundlePackageType 27 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 28 | CFBundleShortVersionString 29 | 1.0 30 | CFBundleVersion 31 | 1 32 | LSRequiresIPhoneOS 33 | 34 | UIApplicationSceneManifest 35 | 36 | UIApplicationSupportsMultipleScenes 37 | 38 | 39 | UIApplicationSupportsIndirectInputEvents 40 | 41 | UILaunchScreen 42 | 43 | UIRequiredDeviceCapabilities 44 | 45 | armv7 46 | 47 | UISupportedInterfaceOrientations 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UISupportedInterfaceOrientations~ipad 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | UIInterfaceOrientationLandscapeLeft 58 | UIInterfaceOrientationLandscapeRight 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /TransNotion/Domain/NotionWebView/NotionWebView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotionWebView.swift 3 | // TransNotion 4 | // 5 | // Created by 廣瀬雄大 on 2021/06/22. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | import WebKit 11 | 12 | struct NotionWebView: UIViewRepresentable { 13 | @Binding var url: URL 14 | @Binding var isLoading: Bool 15 | 16 | @EnvironmentObject var state: TransNotionApp.State 17 | @ObservedObject var observer: Observer = .init() 18 | 19 | func makeUIView(context: Context) -> WKWebView { 20 | let webView = WKWebView(frame: .zero) 21 | webView.allowsBackForwardNavigationGestures = true 22 | webView.navigationDelegate = context.coordinator 23 | 24 | observer.observations.append( 25 | webView.observe(\.isLoading, changeHandler: { webView, change in 26 | DispatchQueue.main.async { 27 | isLoading = webView.isLoading 28 | webView.isUserInteractionEnabled = !webView.isLoading 29 | } 30 | }) 31 | ) 32 | observer.observations.append( 33 | webView.observe(\.url, changeHandler: { webView, _ in 34 | DispatchQueue.main.async { 35 | if let url = webView.url { 36 | self.url = url 37 | } 38 | } 39 | }) 40 | ) 41 | 42 | webView.load(URLRequest(url: url)) 43 | 44 | return webView 45 | } 46 | 47 | func updateUIView(_ uiView: WKWebView, context: Context) { 48 | 49 | } 50 | 51 | // MARK: - Coordinate 52 | final class Coordinator: NSObject { 53 | let webView: NotionWebView 54 | 55 | init(webView: NotionWebView) { 56 | self.webView = webView 57 | } 58 | } 59 | func makeCoordinator() -> Coordinator { 60 | Coordinator(webView: self) 61 | } 62 | 63 | // MARK: - Observer 64 | final class Observer: ObservableObject { 65 | fileprivate var observations: [NSKeyValueObservation] = [] 66 | } 67 | } 68 | 69 | private let allowHosts: [String] = ["www.notion.so", "notion.so"] 70 | extension NotionWebView.Coordinator: WKNavigationDelegate { 71 | public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 72 | guard let url = navigationAction.request.url, let host = url.host, allowHosts.contains(host) else { 73 | decisionHandler(.cancel) 74 | return 75 | } 76 | decisionHandler(.allow) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /TransNotion.xcodeproj/xcshareddata/xcschemes/TransNotion.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /TransNotion/Service/Database/Remote/Database.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Combine 3 | import FirebaseFirestore 4 | import FirebaseFirestoreSwift 5 | import CoreLocation 6 | 7 | protocol Database { 8 | func fetch(path: DatabaseDocumentPathBuilder) -> AnyPublisher 9 | func create(path: DatabaseCollectionPathBuilder, value: T) -> AnyPublisher 10 | func createWithID(path: DatabaseCollectionPathBuilder, value: T, identifier: String) -> AnyPublisher 11 | func update(path: DatabaseDocumentPathBuilder, value: T) -> AnyPublisher 12 | } 13 | 14 | private let database = Firestore.firestore() 15 | struct FirestoreDatabase: Database { 16 | static let shared = FirestoreDatabase() 17 | private init() { } 18 | 19 | // MARK: - Fetch 20 | func fetch(path: DatabaseDocumentPathBuilder) -> AnyPublisher { 21 | database.document(path.path).get() 22 | } 23 | 24 | struct DecodeError { 25 | let index: Int 26 | let error: Error 27 | let data: [String: Any] 28 | } 29 | 30 | // MARK: - Modifier 31 | func create(path: DatabaseCollectionPathBuilder, value: T) -> AnyPublisher { 32 | database.collection(path.path).document().set(from: value) 33 | } 34 | 35 | func createWithID(path: DatabaseCollectionPathBuilder, value: T, identifier: String) -> AnyPublisher { 36 | database.collection(path.path).document(identifier).set(from: value) 37 | } 38 | 39 | func update(path: DatabaseDocumentPathBuilder, value: T) -> AnyPublisher { 40 | database.document(path.path).set(from: value) 41 | } 42 | } 43 | 44 | // MARK: - Private 45 | private extension FirestoreDatabase { 46 | func buildCollectionQuery(for path: DatabaseCollectionPathBuilder) -> FirebaseFirestore.Query { 47 | var query = path.isGroup ? database.collectionGroup(path.path) : database.collection(path.path) 48 | if let args = path.args { 49 | args.relations.forEach { relation in 50 | switch relation { 51 | case let .equal(value): 52 | query = query.whereField(args.key.rawValue, isEqualTo: value) 53 | case let .less(value): 54 | query = query.whereField(args.key.rawValue, isLessThan: value) 55 | case let .lessOrEqual(value): 56 | query = query.whereField(args.key.rawValue, isLessThanOrEqualTo: value) 57 | case let .greater(value): 58 | query = query.whereField(args.key.rawValue, isGreaterThan: value) 59 | case let .greaterOrEqual(value): 60 | query = query.whereField(args.key.rawValue, isGreaterThanOrEqualTo: value) 61 | case let .notEqual(value): 62 | query = query.whereField(args.key.rawValue, isNotEqualTo: value) 63 | case let .contains(values): 64 | query = query.whereField(args.key.rawValue, in: values) 65 | } 66 | } 67 | } 68 | return query 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TransNotion/Domain/Login/LoginView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginView.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/15. 6 | // 7 | 8 | import SwiftUI 9 | import AuthenticationServices 10 | import Combine 11 | 12 | struct LoginView: View { 13 | @ObservedObject var viewModel: ViewModel = .init() 14 | @EnvironmentObject var state: TransNotionApp.State 15 | 16 | var body: some View { 17 | ZStack { 18 | Color.background.edgesIgnoringSafeArea(.all) 19 | 20 | VStack { 21 | Text("TransNotion").font(.title).foregroundColor(.textColor) 22 | Button(action: { 23 | viewModel.startNotionOAuth() 24 | }, label: { 25 | HStack { 26 | Image("notion_icon") 27 | .resizable() 28 | .frame(width: 32, height: 32) 29 | Text("Login with Notion") 30 | .foregroundColor(.black) 31 | } 32 | .frame(width: 240, height: 44) 33 | .background(Color.white) 34 | .cornerRadius(4) 35 | }) 36 | } 37 | } 38 | .onAppear { 39 | viewModel.onAppear(state: state) 40 | } 41 | } 42 | } 43 | 44 | extension LoginView { 45 | class ViewModel: ObservableObject { 46 | @Environment(\.keyWindow) var keyWindow 47 | @Published var credential: Credential? 48 | @Published var error: Error? 49 | var cancellables: [AnyCancellable] = [] 50 | let localStore = LocalStore() 51 | @Published var state: TransNotionApp.State! 52 | 53 | func onAppear(state: TransNotionApp.State) { 54 | self.state = state 55 | } 56 | 57 | func startNotionOAuth() { 58 | guard let window = keyWindow() else { 59 | fatalError("unexpected window is not found") 60 | } 61 | NotionOAuth(window: window) 62 | .start() 63 | .receive(on: DispatchQueue.main) 64 | .sink (receiveCompletion: { [weak self] (completion) in 65 | print("Completion:", completion) 66 | switch completion { 67 | case .failure(let error): 68 | print("Failure:", error) 69 | self?.error = error 70 | return 71 | case .finished: 72 | return 73 | } 74 | }, receiveValue: { [weak self] (credential) in 75 | print("Success:", credential) 76 | self?.credential = credential 77 | self?.store(credential: credential) 78 | }) 79 | .store(in: &cancellables) 80 | } 81 | 82 | private func store(credential: Credential) { 83 | do { 84 | var credentials: Credentials 85 | switch try localStore.read() { 86 | case nil: 87 | credentials = .init(elements: []) 88 | case let _credentials?: 89 | credentials = _credentials 90 | } 91 | 92 | credentials.elements.append(credential) 93 | try localStore.write(for: credentials) 94 | state.isLogin = true 95 | } catch { 96 | errorLogger.record(error: error) 97 | } 98 | } 99 | } 100 | } 101 | 102 | struct LoginView_Previews: PreviewProvider { 103 | static var previews: some View { 104 | LoginView() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /TransNotion/Service/Auth/OAuth.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OAuth.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/16. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import AuthenticationServices 11 | 12 | private let authorizeURL: URL = URL(string: "https://api.notion.com/v1/oauth/authorize?client_id=\(Secret.notionOAuthClientID)&redirect_uri=\(Secret.notionOAuthRedirectURI)&response_type=code")! 13 | private let tokenURL: URL = URL(string: "https://api.notion.com/v1/oauth/token")! 14 | private let basicAuthHeader: String = { 15 | let base = "\(Secret.notionOAuthClientID):\(Secret.notionOAuthClientSecret)" 16 | return base.data(using: .utf8)!.base64EncodedString() 17 | }() 18 | 19 | final class NotionOAuth: NSObject { 20 | let window: ASPresentationAnchor 21 | init(window: UIWindow) { 22 | self.window = window 23 | } 24 | func start() -> AnyPublisher { 25 | requestAuthentification() 26 | .flatMap(requestCredential(code:)) 27 | .eraseToAnyPublisher() 28 | } 29 | } 30 | 31 | extension NotionOAuth { 32 | typealias TemporaryAuthentificationCode = String 33 | enum RequestAuthentificationError: Swift.Error { 34 | case sessionError(Swift.Error) 35 | case codeNotFound 36 | } 37 | 38 | func requestAuthentification() -> AnyPublisher { 39 | Future { completion in 40 | let session = ASWebAuthenticationSession(url: authorizeURL, callbackURLScheme: Secret.notionOAuthCallbackCustomURLScheme) { (url, error) in 41 | if let error = error { 42 | completion(.failure(.sessionError(error))) 43 | } else if let url = url { 44 | completion(.success(url)) 45 | } 46 | } 47 | session.presentationContextProvider = self 48 | session.prefersEphemeralWebBrowserSession = true 49 | session.start() 50 | } 51 | .tryMap { url in 52 | guard let code = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first(where: { $0.name == "code" })?.value else { 53 | throw RequestAuthentificationError.codeNotFound 54 | } 55 | return code 56 | } 57 | .eraseToAnyPublisher() 58 | } 59 | 60 | struct RequestCredentialBody: Encodable { 61 | let grantType: String = "authorization_code" 62 | let code: TemporaryAuthentificationCode 63 | let redirectUri: String = Secret.notionOAuthRedirectURI 64 | } 65 | enum RequestCredentialError: LocalizedError { 66 | case notStableResponse 67 | case emptyData 68 | } 69 | func requestCredential(code: TemporaryAuthentificationCode) -> AnyPublisher { 70 | var request = URLRequest(url: tokenURL) 71 | request.httpMethod = "POST" 72 | request.allHTTPHeaderFields = [ 73 | "Authorization": "Basic \(basicAuthHeader)", 74 | "Content-Type": "application/json", 75 | ] 76 | request.httpBody = try! JSONEncoder 77 | .convertToSnakeCase 78 | .encode( 79 | RequestCredentialBody(code: code) 80 | ) 81 | return URLSession.shared.dataTaskPublisher(for: request) 82 | .tryMap { (data, response) in 83 | guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else { 84 | throw RequestCredentialError.notStableResponse 85 | } 86 | if data.isEmpty { 87 | throw RequestCredentialError.emptyData 88 | } 89 | return data 90 | } 91 | .decode(type: Credential.self, decoder: JSONDecoder.convertFromSnakeCase) 92 | .eraseToAnyPublisher() 93 | } 94 | } 95 | 96 | extension NotionOAuth: ASWebAuthenticationPresentationContextProviding { 97 | func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { 98 | window 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /TransNotion.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "abseil", 6 | "repositoryURL": "https://github.com/firebase/abseil-cpp-SwiftPM.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "ff5f5f0a3d3266836b9d722bb73cabdf7057482e", 10 | "version": "0.20200225.3" 11 | } 12 | }, 13 | { 14 | "package": "BoringSSL-GRPC", 15 | "repositoryURL": "https://github.com/firebase/boringssl-SwiftPM.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "15889fadef7078dfa1392b0e2b08f3d0e9797104", 19 | "version": "0.7.1" 20 | } 21 | }, 22 | { 23 | "package": "Firebase", 24 | "repositoryURL": "https://github.com/firebase/firebase-ios-sdk.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "f2b8de559b084a56f65bff79f4e736b46d0dafd3", 28 | "version": "8.0.0" 29 | } 30 | }, 31 | { 32 | "package": "GoogleAppMeasurement", 33 | "repositoryURL": "https://github.com/google/GoogleAppMeasurement.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "48fcea0c0a74fc89131696aa995043eaa971ccb1", 37 | "version": "8.0.0" 38 | } 39 | }, 40 | { 41 | "package": "GoogleDataTransport", 42 | "repositoryURL": "https://github.com/google/GoogleDataTransport.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "369716b8d7518a530ce3c3a251436d72546debd8", 46 | "version": "8.4.0" 47 | } 48 | }, 49 | { 50 | "package": "GoogleUtilities", 51 | "repositoryURL": "https://github.com/google/GoogleUtilities.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "58359415d4ea29ebdc8fa5b41ce96c434d76d901", 55 | "version": "7.4.1" 56 | } 57 | }, 58 | { 59 | "package": "gRPC", 60 | "repositoryURL": "https://github.com/firebase/grpc-SwiftPM.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "fb405dd2c7901485f7e158b24e3a0a47e4efd8b5", 64 | "version": "1.28.4" 65 | } 66 | }, 67 | { 68 | "package": "GTMSessionFetcher", 69 | "repositoryURL": "https://github.com/google/gtm-session-fetcher.git", 70 | "state": { 71 | "branch": null, 72 | "revision": "91ed3d188eb95705fef3c249453b81f32dc557d1", 73 | "version": "1.5.0" 74 | } 75 | }, 76 | { 77 | "package": "leveldb", 78 | "repositoryURL": "https://github.com/firebase/leveldb.git", 79 | "state": { 80 | "branch": null, 81 | "revision": "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", 82 | "version": "1.22.2" 83 | } 84 | }, 85 | { 86 | "package": "nanopb", 87 | "repositoryURL": "https://github.com/firebase/nanopb.git", 88 | "state": { 89 | "branch": null, 90 | "revision": "7ee9ef9f627d85cbe1b8c4f49a3ed26eed216c77", 91 | "version": "2.30908.0" 92 | } 93 | }, 94 | { 95 | "package": "notion", 96 | "repositoryURL": "git@github.com:noppefoxwolf/notion.git", 97 | "state": { 98 | "branch": null, 99 | "revision": "b90657c73a432f3f3a3191eed6311d2935859861", 100 | "version": "0.1.1" 101 | } 102 | }, 103 | { 104 | "package": "Promises", 105 | "repositoryURL": "https://github.com/google/promises.git", 106 | "state": { 107 | "branch": null, 108 | "revision": "afa9a1ace74e116848d4f743599ab83e584ff8cb", 109 | "version": "1.2.12" 110 | } 111 | }, 112 | { 113 | "package": "SwiftProtobuf", 114 | "repositoryURL": "https://github.com/apple/swift-protobuf.git", 115 | "state": { 116 | "branch": null, 117 | "revision": "1f62db409f2c9b0223a3f68567b4a01333aae778", 118 | "version": "1.17.0" 119 | } 120 | } 121 | ] 122 | }, 123 | "version": 1 124 | } 125 | -------------------------------------------------------------------------------- /TransNotion/Domain/NotionPages/NotionPagesView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotionPagesView.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/17. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | import notion 11 | 12 | struct NotionPagesView: View { 13 | struct URLContainer: Identifiable { 14 | let id = UUID() 15 | let url: URL 16 | } 17 | 18 | @ObservedObject var viewModel: ViewModel = .init() 19 | @State var url: URLContainer? 20 | 21 | var body: some View { 22 | NavigationView { 23 | VStack { 24 | List(viewModel.topPages, children: \.children) { page in 25 | Button(action: { url = page.base.pageURL().map(URLContainer.init(url:)) }, label: { 26 | Toggle(page.base.retrieveTitle()!, isOn: .init(get: { page.isChecked }, set: { viewModel.update(for: page, isChecked: $0) })) 27 | .toggleStyle(CheckBoxToggleStyle()) 28 | .frame(height: 48) 29 | }) 30 | } 31 | .listStyle(PlainListStyle()) 32 | Group { 33 | let targetPages = viewModel.targetPages 34 | Button(!targetPages.isEmpty ? "Translate \(targetPages.count) page" : "Check translate target pages") { 35 | print("TODO: Translate and extract currnet page") 36 | } 37 | .disabled(targetPages.isEmpty) 38 | .buttonStyle(PrimaryButtonStyle(width: .infinity)) 39 | .padding(.horizontal, 20) 40 | } 41 | } 42 | .navigationTitle(Text("Notion Pages")) 43 | } 44 | .accentColor(.appPrimary) 45 | .sheet(item: $url, content: { url in 46 | NotionWebViewPage(url: url.url) 47 | }) 48 | .alert(isPresented: .init(get: { viewModel.error != nil }, set: { _ in viewModel.error = nil })) { 49 | Alert( 50 | title: Text("Error"), 51 | message: Text(viewModel.error?.localizedDescription ?? ""), 52 | dismissButton: Alert.Button.cancel() 53 | ) 54 | } 55 | .onAppear { 56 | viewModel.fetchPages() 57 | } 58 | } 59 | } 60 | 61 | extension NotionPagesView { 62 | final class ViewModel: ObservableObject { 63 | @Environment(\.notion) private var session 64 | @Published var topPages: [Page] = [] 65 | @Published var error: Swift.Error? 66 | var cancellables: [AnyCancellable] = [] 67 | var allPages: [Page] = [] 68 | var targetPages: [Page] { 69 | allPages.filter(\.isChecked) 70 | } 71 | 72 | class Page: Identifiable { 73 | var id: String { base.id } 74 | let base: Object.Page 75 | var children: [Page]? 76 | var isChecked: Bool = false 77 | init(base: Object.Page) { 78 | self.base = base 79 | } 80 | } 81 | 82 | func update(for page: Page, isChecked: Bool) { 83 | page.isChecked = isChecked 84 | // Call Published 85 | topPages = topPages 86 | } 87 | 88 | func fetchPages() { 89 | session.send(V1.Search.Search(query: "")).sink { [weak self] result in 90 | switch result { 91 | case let .success(response): 92 | let notionPages: [Object.Page] = response.results.compactMap { 93 | if case let .page(page) = $0.object { 94 | return page 95 | } else { 96 | print("[INFO]", $0.object) 97 | return nil 98 | } 99 | } 100 | 101 | let allPages: [Page] = notionPages.map(Page.init(base:)) 102 | for page in allPages { 103 | for child in allPages { 104 | if case let .pageId(parentID) = child.base.parent.type { 105 | if page.id == parentID { 106 | if page.children == nil { 107 | page.children = [] 108 | } 109 | page.children?.append(child) 110 | } 111 | } 112 | } 113 | } 114 | let topPages = allPages.filter { page in 115 | switch page.base.parent.type { 116 | case .databaseId, .workspace: 117 | return true 118 | case .pageId: 119 | return false 120 | } 121 | } 122 | self?.allPages = allPages 123 | self?.topPages = topPages 124 | case let .failure(error): 125 | self?.error = error 126 | } 127 | }.store(in: &cancellables) 128 | } 129 | 130 | } 131 | } 132 | 133 | struct NotionPagesView_Previews: PreviewProvider { 134 | static var previews: some View { 135 | NotionPagesView() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /TransNotion/Domain/NotionPage/NotionPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotionPageView.swift 3 | // TransNotion 4 | // 5 | // Created by Yudai.Hirose on 2021/05/17. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | import notion 11 | 12 | struct NotionPageView: View { 13 | @ObservedObject var viewModel: ViewModel 14 | 15 | var body: some View { 16 | List { 17 | ForEach(viewModel.blocks) { block in 18 | plainText(block: block) 19 | } 20 | } 21 | .navigationTitle(viewModel.page?.retrieveTitle() ?? "Loading") 22 | .navigationBarItems(trailing: Button(action: { 23 | viewModel.isPresentedMenuSheet = true 24 | }, label: { 25 | Image(systemName: "plus") 26 | })) 27 | .onAppear { 28 | viewModel.fetch() 29 | viewModel.fetchBlocks() 30 | } 31 | .alert(isPresented: .init(get: { viewModel.error != nil }, set: { _ in viewModel.error = nil })) { 32 | Alert( 33 | title: Text("Error"), 34 | message: Text(viewModel.error?.localizedDescription ?? ""), 35 | dismissButton: Alert.Button.cancel() 36 | ) 37 | } 38 | .actionSheet(isPresented: $viewModel.isPresentedMenuSheet, content: { 39 | ActionSheet(title: Text("menu"), buttons: [ 40 | .default(Text("Add Block"), action: { 41 | viewModel.createBlock() 42 | }), 43 | .default(Text("Update Property"), action: { 44 | viewModel.updateProperty() 45 | }), 46 | .cancel() 47 | ]) 48 | }) 49 | } 50 | 51 | @ViewBuilder 52 | func plainText(block: Block) -> some View { 53 | switch block.type { 54 | case let .paragraph(paragraph): 55 | Text(paragraph.text.first?.plainText ?? "") 56 | case let .heading1(heading1): 57 | Text(heading1.text.first?.plainText ?? "") 58 | case let .heading2(heading2): 59 | Text(heading2.text.first?.plainText ?? "") 60 | case let .heading3(heading3): 61 | Text(heading3.text.first?.plainText ?? "") 62 | case let .bulletedListItem(bulletedListItem): 63 | Text(bulletedListItem.text.first?.plainText ?? "") 64 | case let .numberedListItem(numberedListItem): 65 | Text(numberedListItem.text.first?.plainText ?? "") 66 | case let .toDo(toDo): 67 | Text(toDo.text.first?.plainText ?? "") 68 | case let .toggle(toggle): 69 | Text(toggle.text.first?.plainText ?? "") 70 | case let .childPage(childPage): 71 | Text(childPage.title) 72 | case .unsupported: 73 | Text("-") 74 | } 75 | } 76 | } 77 | 78 | extension NotionPageView { 79 | class ViewModel: ObservableObject { 80 | internal init(id: Page.ID) { 81 | self.id = id 82 | } 83 | 84 | @Environment(\.notion) private var session 85 | @Published var page: Object.Page? 86 | @Published var blocks: [Object.Block] = [] 87 | @Published var error: Error? = nil 88 | var cancellables: [AnyCancellable] = [] 89 | @Published var isPresentedMenuSheet: Bool = false 90 | 91 | let id: Page.ID 92 | 93 | func fetch() { 94 | session.send(V1.Pages.Page(id: id)).sink { result in 95 | switch result { 96 | case let .success(response): 97 | print(response.properties) 98 | self.page = response 99 | case let .failure(error): 100 | self.error = error 101 | } 102 | }.store(in: &cancellables) 103 | } 104 | 105 | func fetchBlocks() { 106 | session.send(V1.Blocks.Children(id: id)).sink { result in 107 | switch result { 108 | case let .success(response): 109 | self.blocks = response.results 110 | case let .failure(error): 111 | self.error = error 112 | } 113 | }.store(in: &cancellables) 114 | } 115 | 116 | func createBlock() { 117 | let block: Block = .init(type: .heading1(.init(text: [.init(type: .text(.init(content: "append line")))]))) 118 | session.send(V1.Blocks.Append(id: id, children: [block])).sink { result in 119 | switch result { 120 | case let .success(response): 121 | self.blocks = [] 122 | self.fetchBlocks() 123 | case let .failure(error): 124 | self.error = error 125 | } 126 | }.store(in: &cancellables) 127 | } 128 | 129 | func updateProperty() { 130 | session.send(V1.Pages.Update(id: id, properties: ["title" : .init(type: .title([.init(type: .text(.init(content: "Updated name")))]))])).sink { result in 131 | switch result { 132 | case let .success(response): 133 | self.page = nil 134 | self.fetch() 135 | break 136 | case let .failure(error): 137 | self.error = error 138 | } 139 | }.store(in: &cancellables) 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /TransNotion.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 20F7E1162681F65A00F0024A /* NotionWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E1152681F65A00F0024A /* NotionWebView.swift */; }; 11 | 20F7E1182681FDF400F0024A /* Notion.Object.Page+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E1172681FDF400F0024A /* Notion.Object.Page+Extension.swift */; }; 12 | 20F7E11A26820EA500F0024A /* NotionWebViewPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E11926820EA500F0024A /* NotionWebViewPage.swift */; }; 13 | 20F7E11D2682145A00F0024A /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E11C2682145A00F0024A /* Placeholder.swift */; }; 14 | 20F7E1202682287400F0024A /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E11F2682287400F0024A /* Button.swift */; }; 15 | 20F7E1222682CC5B00F0024A /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F7E1212682CC5B00F0024A /* Toggle.swift */; }; 16 | BA00718D26508564001DFFCB /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = BA00718926508564001DFFCB /* Debug.xcconfig */; }; 17 | BA00718E26508564001DFFCB /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = BA00718A26508564001DFFCB /* Release.xcconfig */; }; 18 | BA00718F26508564001DFFCB /* Debug-Secret.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = BA00718B26508564001DFFCB /* Debug-Secret.xcconfig */; }; 19 | BA00719026508564001DFFCB /* Release-Secret.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = BA00718C26508564001DFFCB /* Release-Secret.xcconfig */; }; 20 | BA00719F2650886C001DFFCB /* OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA00719E2650886C001DFFCB /* OAuth.swift */; }; 21 | BA0071A826509001001DFFCB /* Environment+Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0071A726509001001DFFCB /* Environment+Window.swift */; }; 22 | BA0071E92650A401001DFFCB /* GoogleService-Info-dev.plist in Resources */ = {isa = PBXBuildFile; fileRef = BAD32C20264FC6E600DD7824 /* GoogleService-Info-dev.plist */; }; 23 | BA0071F126511CFC001DFFCB /* LocalStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0071F026511CFC001DFFCB /* LocalStore.swift */; }; 24 | BA0071FC26512C9C001DFFCB /* Credential.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0071FB26512C9C001DFFCB /* Credential.swift */; }; 25 | BA00720226512D63001DFFCB /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA00720126512D63001DFFCB /* ErrorLogger.swift */; }; 26 | BA00720B26512E89001DFFCB /* JSONCoder+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA00720A26512E89001DFFCB /* JSONCoder+Extension.swift */; }; 27 | BA00721D2651C99C001DFFCB /* notion in Frameworks */ = {isa = PBXBuildFile; productRef = BA00721C2651C99C001DFFCB /* notion */; }; 28 | BA00722C2651C9E6001DFFCB /* NotionPagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA00722B2651C9E6001DFFCB /* NotionPagesView.swift */; }; 29 | BA0072342651CB05001DFFCB /* Environment+notion.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0072332651CB05001DFFCB /* Environment+notion.swift */; }; 30 | BA0B34C4264FA4F100774AA0 /* TransNotionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0B34C3264FA4F100774AA0 /* TransNotionApp.swift */; }; 31 | BA0B34C6264FA4F100774AA0 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0B34C5264FA4F100774AA0 /* RootView.swift */; }; 32 | BA0B34C8264FA4F800774AA0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BA0B34C7264FA4F800774AA0 /* Assets.xcassets */; }; 33 | BA0B34CB264FA4F800774AA0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BA0B34CA264FA4F800774AA0 /* Preview Assets.xcassets */; }; 34 | BA0B34D6264FA4F800774AA0 /* TransNotionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0B34D5264FA4F800774AA0 /* TransNotionTests.swift */; }; 35 | BA0B34E1264FA4F800774AA0 /* TransNotionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0B34E0264FA4F800774AA0 /* TransNotionUITests.swift */; }; 36 | BA842EBF2651DF0400399BDB /* NotionPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA842EBE2651DF0400399BDB /* NotionPage.swift */; }; 37 | BACEC6BD264FF4AC0070D31B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6BC264FF4AC0070D31B /* ContentView.swift */; }; 38 | BACEC6C2264FF4B90070D31B /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6C1264FF4B90070D31B /* LoginView.swift */; }; 39 | BACEC6CE264FF5030070D31B /* DatabasePathBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6C8264FF5020070D31B /* DatabasePathBuilder.swift */; }; 40 | BACEC6CF264FF5030070D31B /* DatabaseEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6C9264FF5020070D31B /* DatabaseEntity.swift */; }; 41 | BACEC6D0264FF5030070D31B /* DocumentReference+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6CA264FF5020070D31B /* DocumentReference+Combine.swift */; }; 42 | BACEC6D1264FF5030070D31B /* CollectionReference+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6CB264FF5020070D31B /* CollectionReference+Combine.swift */; }; 43 | BACEC6D2264FF5030070D31B /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6CC264FF5020070D31B /* Database.swift */; }; 44 | BACEC6D3264FF5030070D31B /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6CD264FF5020070D31B /* Auth.swift */; }; 45 | BACEC6DA264FF54B0070D31B /* Me.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6D8264FF54B0070D31B /* Me.swift */; }; 46 | BACEC6DB264FF54B0070D31B /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6D9264FF54B0070D31B /* User.swift */; }; 47 | BACEC6E1264FF7EB0070D31B /* Color+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BACEC6E0264FF7EB0070D31B /* Color+Extension.swift */; }; 48 | BACEC6EF264FF9430070D31B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = BACEC6F1264FF9430070D31B /* Localizable.strings */; }; 49 | BAD32BE3264FC50B00DD7824 /* Secret.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD32BE2264FC50B00DD7824 /* Secret.swift */; }; 50 | BAD32C3F264FCE5C00DD7824 /* FirebaseFirestoreSwift-Beta in Frameworks */ = {isa = PBXBuildFile; productRef = BAD32C3E264FCE5C00DD7824 /* FirebaseFirestoreSwift-Beta */; }; 51 | BAD32C41264FCE5C00DD7824 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = BAD32C40264FCE5C00DD7824 /* FirebaseCrashlytics */; }; 52 | BAD32C43264FCE5C00DD7824 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = BAD32C42264FCE5C00DD7824 /* FirebaseAuth */; }; 53 | BAD32C45264FCE5C00DD7824 /* FirebaseAnalyticsSwift-Beta in Frameworks */ = {isa = PBXBuildFile; productRef = BAD32C44264FCE5C00DD7824 /* FirebaseAnalyticsSwift-Beta */; }; 54 | BAD32C47264FCE5C00DD7824 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = BAD32C46264FCE5C00DD7824 /* FirebaseFirestore */; }; 55 | BAD32C4D264FDDAB00DD7824 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD32C4C264FDDAB00DD7824 /* AppDelegate.swift */; }; 56 | /* End PBXBuildFile section */ 57 | 58 | /* Begin PBXContainerItemProxy section */ 59 | BA0B34D2264FA4F800774AA0 /* PBXContainerItemProxy */ = { 60 | isa = PBXContainerItemProxy; 61 | containerPortal = BA0B34B8264FA4F100774AA0 /* Project object */; 62 | proxyType = 1; 63 | remoteGlobalIDString = BA0B34BF264FA4F100774AA0; 64 | remoteInfo = TransNotion; 65 | }; 66 | BA0B34DD264FA4F800774AA0 /* PBXContainerItemProxy */ = { 67 | isa = PBXContainerItemProxy; 68 | containerPortal = BA0B34B8264FA4F100774AA0 /* Project object */; 69 | proxyType = 1; 70 | remoteGlobalIDString = BA0B34BF264FA4F100774AA0; 71 | remoteInfo = TransNotion; 72 | }; 73 | /* End PBXContainerItemProxy section */ 74 | 75 | /* Begin PBXFileReference section */ 76 | 20F7E1152681F65A00F0024A /* NotionWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotionWebView.swift; sourceTree = ""; }; 77 | 20F7E1172681FDF400F0024A /* Notion.Object.Page+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notion.Object.Page+Extension.swift"; sourceTree = ""; }; 78 | 20F7E11926820EA500F0024A /* NotionWebViewPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotionWebViewPage.swift; sourceTree = ""; }; 79 | 20F7E11C2682145A00F0024A /* Placeholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Placeholder.swift; sourceTree = ""; }; 80 | 20F7E11F2682287400F0024A /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 81 | 20F7E1212682CC5B00F0024A /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; 82 | BA00718926508564001DFFCB /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 83 | BA00718A26508564001DFFCB /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 84 | BA00718B26508564001DFFCB /* Debug-Secret.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Debug-Secret.xcconfig"; sourceTree = ""; }; 85 | BA00718C26508564001DFFCB /* Release-Secret.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Release-Secret.xcconfig"; sourceTree = ""; }; 86 | BA00719E2650886C001DFFCB /* OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth.swift; sourceTree = ""; }; 87 | BA0071A726509001001DFFCB /* Environment+Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+Window.swift"; sourceTree = ""; }; 88 | BA0071F026511CFC001DFFCB /* LocalStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStore.swift; sourceTree = ""; }; 89 | BA0071FB26512C9C001DFFCB /* Credential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credential.swift; sourceTree = ""; }; 90 | BA00720126512D63001DFFCB /* ErrorLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogger.swift; sourceTree = ""; }; 91 | BA00720A26512E89001DFFCB /* JSONCoder+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONCoder+Extension.swift"; sourceTree = ""; }; 92 | BA00722B2651C9E6001DFFCB /* NotionPagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotionPagesView.swift; sourceTree = ""; }; 93 | BA0072332651CB05001DFFCB /* Environment+notion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+notion.swift"; sourceTree = ""; }; 94 | BA0B34C0264FA4F100774AA0 /* TransNotion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TransNotion.app; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | BA0B34C3264FA4F100774AA0 /* TransNotionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransNotionApp.swift; sourceTree = ""; }; 96 | BA0B34C5264FA4F100774AA0 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; 97 | BA0B34C7264FA4F800774AA0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 98 | BA0B34CA264FA4F800774AA0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 99 | BA0B34CC264FA4F800774AA0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | BA0B34D1264FA4F800774AA0 /* TransNotionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TransNotionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | BA0B34D5264FA4F800774AA0 /* TransNotionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransNotionTests.swift; sourceTree = ""; }; 102 | BA0B34D7264FA4F800774AA0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 103 | BA0B34DC264FA4F800774AA0 /* TransNotionUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TransNotionUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | BA0B34E0264FA4F800774AA0 /* TransNotionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransNotionUITests.swift; sourceTree = ""; }; 105 | BA0B34E2264FA4F800774AA0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | BA842EBE2651DF0400399BDB /* NotionPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotionPage.swift; sourceTree = ""; }; 107 | BACEC6BC264FF4AC0070D31B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 108 | BACEC6C1264FF4B90070D31B /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 109 | BACEC6C8264FF5020070D31B /* DatabasePathBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePathBuilder.swift; sourceTree = ""; }; 110 | BACEC6C9264FF5020070D31B /* DatabaseEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseEntity.swift; sourceTree = ""; }; 111 | BACEC6CA264FF5020070D31B /* DocumentReference+Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DocumentReference+Combine.swift"; sourceTree = ""; }; 112 | BACEC6CB264FF5020070D31B /* CollectionReference+Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CollectionReference+Combine.swift"; sourceTree = ""; }; 113 | BACEC6CC264FF5020070D31B /* Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; 114 | BACEC6CD264FF5020070D31B /* Auth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; 115 | BACEC6D8264FF54B0070D31B /* Me.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Me.swift; sourceTree = ""; }; 116 | BACEC6D9264FF54B0070D31B /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 117 | BACEC6E0264FF7EB0070D31B /* Color+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extension.swift"; sourceTree = ""; }; 118 | BACEC6F0264FF9430070D31B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 119 | BACEC6F5264FF9690070D31B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; 120 | BAD32BE2264FC50B00DD7824 /* Secret.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Secret.swift; sourceTree = ""; }; 121 | BAD32C20264FC6E600DD7824 /* GoogleService-Info-dev.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info-dev.plist"; sourceTree = ""; }; 122 | BAD32C26264FC96000DD7824 /* Secret.swift.sample */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Secret.swift.sample; sourceTree = ""; }; 123 | BAD32C4C264FDDAB00DD7824 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 124 | /* End PBXFileReference section */ 125 | 126 | /* Begin PBXFrameworksBuildPhase section */ 127 | BA0B34BD264FA4F100774AA0 /* Frameworks */ = { 128 | isa = PBXFrameworksBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | BAD32C3F264FCE5C00DD7824 /* FirebaseFirestoreSwift-Beta in Frameworks */, 132 | BAD32C43264FCE5C00DD7824 /* FirebaseAuth in Frameworks */, 133 | BAD32C45264FCE5C00DD7824 /* FirebaseAnalyticsSwift-Beta in Frameworks */, 134 | BAD32C47264FCE5C00DD7824 /* FirebaseFirestore in Frameworks */, 135 | BAD32C41264FCE5C00DD7824 /* FirebaseCrashlytics in Frameworks */, 136 | BA00721D2651C99C001DFFCB /* notion in Frameworks */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | BA0B34CE264FA4F800774AA0 /* Frameworks */ = { 141 | isa = PBXFrameworksBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | BA0B34D9264FA4F800774AA0 /* Frameworks */ = { 148 | isa = PBXFrameworksBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXFrameworksBuildPhase section */ 155 | 156 | /* Begin PBXGroup section */ 157 | 20F7E1142681F64C00F0024A /* NotionWebView */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 20F7E1152681F65A00F0024A /* NotionWebView.swift */, 161 | 20F7E11926820EA500F0024A /* NotionWebViewPage.swift */, 162 | ); 163 | path = NotionWebView; 164 | sourceTree = ""; 165 | }; 166 | 20F7E11B2682145500F0024A /* Modifier */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 20F7E11C2682145A00F0024A /* Placeholder.swift */, 170 | ); 171 | path = Modifier; 172 | sourceTree = ""; 173 | }; 174 | BA00719D26508863001DFFCB /* Auth */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | BACEC6CD264FF5020070D31B /* Auth.swift */, 178 | BA00719E2650886C001DFFCB /* OAuth.swift */, 179 | ); 180 | path = Auth; 181 | sourceTree = ""; 182 | }; 183 | BA0071A626508FFC001DFFCB /* Environment */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | BA0071A726509001001DFFCB /* Environment+Window.swift */, 187 | BA0072332651CB05001DFFCB /* Environment+notion.swift */, 188 | ); 189 | path = Environment; 190 | sourceTree = ""; 191 | }; 192 | BA0071ED26511CDB001DFFCB /* Remote */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | BACEC6C8264FF5020070D31B /* DatabasePathBuilder.swift */, 196 | BACEC6C9264FF5020070D31B /* DatabaseEntity.swift */, 197 | BACEC6CA264FF5020070D31B /* DocumentReference+Combine.swift */, 198 | BACEC6CB264FF5020070D31B /* CollectionReference+Combine.swift */, 199 | BACEC6CC264FF5020070D31B /* Database.swift */, 200 | ); 201 | path = Remote; 202 | sourceTree = ""; 203 | }; 204 | BA0071EE26511CED001DFFCB /* Local */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | BA0071F026511CFC001DFFCB /* LocalStore.swift */, 208 | ); 209 | path = Local; 210 | sourceTree = ""; 211 | }; 212 | BA00720026512D5D001DFFCB /* Logger */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | BA00720126512D63001DFFCB /* ErrorLogger.swift */, 216 | ); 217 | path = Logger; 218 | sourceTree = ""; 219 | }; 220 | BA00720926512E7E001DFFCB /* Extension */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | BA00720A26512E89001DFFCB /* JSONCoder+Extension.swift */, 224 | 20F7E1172681FDF400F0024A /* Notion.Object.Page+Extension.swift */, 225 | ); 226 | path = Extension; 227 | sourceTree = ""; 228 | }; 229 | BA0072212651C9B1001DFFCB /* Domain */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | 20F7E1142681F64C00F0024A /* NotionWebView */, 233 | BA842EBD2651DEF800399BDB /* NotionPage */, 234 | BA0072292651C9C4001DFFCB /* NotionPages */, 235 | BA0072252651C9B4001DFFCB /* Login */, 236 | ); 237 | path = Domain; 238 | sourceTree = ""; 239 | }; 240 | BA0072252651C9B4001DFFCB /* Login */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | BACEC6C1264FF4B90070D31B /* LoginView.swift */, 244 | ); 245 | path = Login; 246 | sourceTree = ""; 247 | }; 248 | BA0072292651C9C4001DFFCB /* NotionPages */ = { 249 | isa = PBXGroup; 250 | children = ( 251 | BA00722B2651C9E6001DFFCB /* NotionPagesView.swift */, 252 | ); 253 | path = NotionPages; 254 | sourceTree = ""; 255 | }; 256 | BA0B34B7264FA4F100774AA0 = { 257 | isa = PBXGroup; 258 | children = ( 259 | BA0B34C2264FA4F100774AA0 /* TransNotion */, 260 | BA0B34D4264FA4F800774AA0 /* TransNotionTests */, 261 | BA0B34DF264FA4F800774AA0 /* TransNotionUITests */, 262 | BA0B34C1264FA4F100774AA0 /* Products */, 263 | ); 264 | sourceTree = ""; 265 | }; 266 | BA0B34C1264FA4F100774AA0 /* Products */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | BA0B34C0264FA4F100774AA0 /* TransNotion.app */, 270 | BA0B34D1264FA4F800774AA0 /* TransNotionTests.xctest */, 271 | BA0B34DC264FA4F800774AA0 /* TransNotionUITests.xctest */, 272 | ); 273 | name = Products; 274 | sourceTree = ""; 275 | }; 276 | BA0B34C2264FA4F100774AA0 /* TransNotion */ = { 277 | isa = PBXGroup; 278 | children = ( 279 | 20F7E11B2682145500F0024A /* Modifier */, 280 | BA00720926512E7E001DFFCB /* Extension */, 281 | BA0071A626508FFC001DFFCB /* Environment */, 282 | BACEC6DF264FF7E10070D31B /* Style */, 283 | BACEC6D7264FF53A0070D31B /* Entity */, 284 | BACEC6C6264FF4ED0070D31B /* Service */, 285 | BA0B34CC264FA4F800774AA0 /* Info.plist */, 286 | BAD32C20264FC6E600DD7824 /* GoogleService-Info-dev.plist */, 287 | BAD32BE2264FC50B00DD7824 /* Secret.swift */, 288 | BAD32C26264FC96000DD7824 /* Secret.swift.sample */, 289 | BA0B34C3264FA4F100774AA0 /* TransNotionApp.swift */, 290 | BAD32C4C264FDDAB00DD7824 /* AppDelegate.swift */, 291 | BA0B34C5264FA4F100774AA0 /* RootView.swift */, 292 | BACEC6BC264FF4AC0070D31B /* ContentView.swift */, 293 | BA0072212651C9B1001DFFCB /* Domain */, 294 | BA0B34C7264FA4F800774AA0 /* Assets.xcassets */, 295 | BA00718B26508564001DFFCB /* Debug-Secret.xcconfig */, 296 | BA00718926508564001DFFCB /* Debug.xcconfig */, 297 | BA00718C26508564001DFFCB /* Release-Secret.xcconfig */, 298 | BA00718A26508564001DFFCB /* Release.xcconfig */, 299 | BA0B34C9264FA4F800774AA0 /* Preview Content */, 300 | BACEC6F1264FF9430070D31B /* Localizable.strings */, 301 | ); 302 | path = TransNotion; 303 | sourceTree = ""; 304 | }; 305 | BA0B34C9264FA4F800774AA0 /* Preview Content */ = { 306 | isa = PBXGroup; 307 | children = ( 308 | BA0B34CA264FA4F800774AA0 /* Preview Assets.xcassets */, 309 | ); 310 | path = "Preview Content"; 311 | sourceTree = ""; 312 | }; 313 | BA0B34D4264FA4F800774AA0 /* TransNotionTests */ = { 314 | isa = PBXGroup; 315 | children = ( 316 | BA0B34D5264FA4F800774AA0 /* TransNotionTests.swift */, 317 | BA0B34D7264FA4F800774AA0 /* Info.plist */, 318 | ); 319 | path = TransNotionTests; 320 | sourceTree = ""; 321 | }; 322 | BA0B34DF264FA4F800774AA0 /* TransNotionUITests */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | BA0B34E0264FA4F800774AA0 /* TransNotionUITests.swift */, 326 | BA0B34E2264FA4F800774AA0 /* Info.plist */, 327 | ); 328 | path = TransNotionUITests; 329 | sourceTree = ""; 330 | }; 331 | BA842EBD2651DEF800399BDB /* NotionPage */ = { 332 | isa = PBXGroup; 333 | children = ( 334 | BA842EBE2651DF0400399BDB /* NotionPage.swift */, 335 | ); 336 | path = NotionPage; 337 | sourceTree = ""; 338 | }; 339 | BACEC6C6264FF4ED0070D31B /* Service */ = { 340 | isa = PBXGroup; 341 | children = ( 342 | BA00720026512D5D001DFFCB /* Logger */, 343 | BA00719D26508863001DFFCB /* Auth */, 344 | BACEC6C7264FF5020070D31B /* Database */, 345 | ); 346 | path = Service; 347 | sourceTree = ""; 348 | }; 349 | BACEC6C7264FF5020070D31B /* Database */ = { 350 | isa = PBXGroup; 351 | children = ( 352 | BA0071EE26511CED001DFFCB /* Local */, 353 | BA0071ED26511CDB001DFFCB /* Remote */, 354 | ); 355 | path = Database; 356 | sourceTree = ""; 357 | }; 358 | BACEC6D7264FF53A0070D31B /* Entity */ = { 359 | isa = PBXGroup; 360 | children = ( 361 | BACEC6D8264FF54B0070D31B /* Me.swift */, 362 | BACEC6D9264FF54B0070D31B /* User.swift */, 363 | BA0071FB26512C9C001DFFCB /* Credential.swift */, 364 | ); 365 | path = Entity; 366 | sourceTree = ""; 367 | }; 368 | BACEC6DF264FF7E10070D31B /* Style */ = { 369 | isa = PBXGroup; 370 | children = ( 371 | BACEC6E0264FF7EB0070D31B /* Color+Extension.swift */, 372 | 20F7E11F2682287400F0024A /* Button.swift */, 373 | 20F7E1212682CC5B00F0024A /* Toggle.swift */, 374 | ); 375 | path = Style; 376 | sourceTree = ""; 377 | }; 378 | /* End PBXGroup section */ 379 | 380 | /* Begin PBXNativeTarget section */ 381 | BA0B34BF264FA4F100774AA0 /* TransNotion */ = { 382 | isa = PBXNativeTarget; 383 | buildConfigurationList = BA0B34E5264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotion" */; 384 | buildPhases = ( 385 | BA0B34BC264FA4F100774AA0 /* Sources */, 386 | BA0B34BD264FA4F100774AA0 /* Frameworks */, 387 | BA0B34BE264FA4F100774AA0 /* Resources */, 388 | ); 389 | buildRules = ( 390 | ); 391 | dependencies = ( 392 | ); 393 | name = TransNotion; 394 | packageProductDependencies = ( 395 | BAD32C3E264FCE5C00DD7824 /* FirebaseFirestoreSwift-Beta */, 396 | BAD32C40264FCE5C00DD7824 /* FirebaseCrashlytics */, 397 | BAD32C42264FCE5C00DD7824 /* FirebaseAuth */, 398 | BAD32C44264FCE5C00DD7824 /* FirebaseAnalyticsSwift-Beta */, 399 | BAD32C46264FCE5C00DD7824 /* FirebaseFirestore */, 400 | BA00721C2651C99C001DFFCB /* notion */, 401 | ); 402 | productName = TransNotion; 403 | productReference = BA0B34C0264FA4F100774AA0 /* TransNotion.app */; 404 | productType = "com.apple.product-type.application"; 405 | }; 406 | BA0B34D0264FA4F800774AA0 /* TransNotionTests */ = { 407 | isa = PBXNativeTarget; 408 | buildConfigurationList = BA0B34E8264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotionTests" */; 409 | buildPhases = ( 410 | BA0B34CD264FA4F800774AA0 /* Sources */, 411 | BA0B34CE264FA4F800774AA0 /* Frameworks */, 412 | BA0B34CF264FA4F800774AA0 /* Resources */, 413 | ); 414 | buildRules = ( 415 | ); 416 | dependencies = ( 417 | BA0B34D3264FA4F800774AA0 /* PBXTargetDependency */, 418 | ); 419 | name = TransNotionTests; 420 | productName = TransNotionTests; 421 | productReference = BA0B34D1264FA4F800774AA0 /* TransNotionTests.xctest */; 422 | productType = "com.apple.product-type.bundle.unit-test"; 423 | }; 424 | BA0B34DB264FA4F800774AA0 /* TransNotionUITests */ = { 425 | isa = PBXNativeTarget; 426 | buildConfigurationList = BA0B34EB264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotionUITests" */; 427 | buildPhases = ( 428 | BA0B34D8264FA4F800774AA0 /* Sources */, 429 | BA0B34D9264FA4F800774AA0 /* Frameworks */, 430 | BA0B34DA264FA4F800774AA0 /* Resources */, 431 | ); 432 | buildRules = ( 433 | ); 434 | dependencies = ( 435 | BA0B34DE264FA4F800774AA0 /* PBXTargetDependency */, 436 | ); 437 | name = TransNotionUITests; 438 | productName = TransNotionUITests; 439 | productReference = BA0B34DC264FA4F800774AA0 /* TransNotionUITests.xctest */; 440 | productType = "com.apple.product-type.bundle.ui-testing"; 441 | }; 442 | /* End PBXNativeTarget section */ 443 | 444 | /* Begin PBXProject section */ 445 | BA0B34B8264FA4F100774AA0 /* Project object */ = { 446 | isa = PBXProject; 447 | attributes = { 448 | LastSwiftUpdateCheck = 1240; 449 | LastUpgradeCheck = 1240; 450 | TargetAttributes = { 451 | BA0B34BF264FA4F100774AA0 = { 452 | CreatedOnToolsVersion = 12.4; 453 | }; 454 | BA0B34D0264FA4F800774AA0 = { 455 | CreatedOnToolsVersion = 12.4; 456 | TestTargetID = BA0B34BF264FA4F100774AA0; 457 | }; 458 | BA0B34DB264FA4F800774AA0 = { 459 | CreatedOnToolsVersion = 12.4; 460 | TestTargetID = BA0B34BF264FA4F100774AA0; 461 | }; 462 | }; 463 | }; 464 | buildConfigurationList = BA0B34BB264FA4F100774AA0 /* Build configuration list for PBXProject "TransNotion" */; 465 | compatibilityVersion = "Xcode 9.3"; 466 | developmentRegion = en; 467 | hasScannedForEncodings = 0; 468 | knownRegions = ( 469 | en, 470 | Base, 471 | ja, 472 | ); 473 | mainGroup = BA0B34B7264FA4F100774AA0; 474 | packageReferences = ( 475 | BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, 476 | BA00721B2651C99C001DFFCB /* XCRemoteSwiftPackageReference "notion" */, 477 | ); 478 | productRefGroup = BA0B34C1264FA4F100774AA0 /* Products */; 479 | projectDirPath = ""; 480 | projectRoot = ""; 481 | targets = ( 482 | BA0B34BF264FA4F100774AA0 /* TransNotion */, 483 | BA0B34D0264FA4F800774AA0 /* TransNotionTests */, 484 | BA0B34DB264FA4F800774AA0 /* TransNotionUITests */, 485 | ); 486 | }; 487 | /* End PBXProject section */ 488 | 489 | /* Begin PBXResourcesBuildPhase section */ 490 | BA0B34BE264FA4F100774AA0 /* Resources */ = { 491 | isa = PBXResourcesBuildPhase; 492 | buildActionMask = 2147483647; 493 | files = ( 494 | BA00718D26508564001DFFCB /* Debug.xcconfig in Resources */, 495 | BA0071E92650A401001DFFCB /* GoogleService-Info-dev.plist in Resources */, 496 | BA0B34CB264FA4F800774AA0 /* Preview Assets.xcassets in Resources */, 497 | BACEC6EF264FF9430070D31B /* Localizable.strings in Resources */, 498 | BA0B34C8264FA4F800774AA0 /* Assets.xcassets in Resources */, 499 | BA00718F26508564001DFFCB /* Debug-Secret.xcconfig in Resources */, 500 | BA00719026508564001DFFCB /* Release-Secret.xcconfig in Resources */, 501 | BA00718E26508564001DFFCB /* Release.xcconfig in Resources */, 502 | ); 503 | runOnlyForDeploymentPostprocessing = 0; 504 | }; 505 | BA0B34CF264FA4F800774AA0 /* Resources */ = { 506 | isa = PBXResourcesBuildPhase; 507 | buildActionMask = 2147483647; 508 | files = ( 509 | ); 510 | runOnlyForDeploymentPostprocessing = 0; 511 | }; 512 | BA0B34DA264FA4F800774AA0 /* Resources */ = { 513 | isa = PBXResourcesBuildPhase; 514 | buildActionMask = 2147483647; 515 | files = ( 516 | ); 517 | runOnlyForDeploymentPostprocessing = 0; 518 | }; 519 | /* End PBXResourcesBuildPhase section */ 520 | 521 | /* Begin PBXSourcesBuildPhase section */ 522 | BA0B34BC264FA4F100774AA0 /* Sources */ = { 523 | isa = PBXSourcesBuildPhase; 524 | buildActionMask = 2147483647; 525 | files = ( 526 | BA0071F126511CFC001DFFCB /* LocalStore.swift in Sources */, 527 | BA842EBF2651DF0400399BDB /* NotionPage.swift in Sources */, 528 | BACEC6C2264FF4B90070D31B /* LoginView.swift in Sources */, 529 | BACEC6E1264FF7EB0070D31B /* Color+Extension.swift in Sources */, 530 | 20F7E1202682287400F0024A /* Button.swift in Sources */, 531 | BACEC6D2264FF5030070D31B /* Database.swift in Sources */, 532 | BA00720B26512E89001DFFCB /* JSONCoder+Extension.swift in Sources */, 533 | BA0072342651CB05001DFFCB /* Environment+notion.swift in Sources */, 534 | 20F7E11D2682145A00F0024A /* Placeholder.swift in Sources */, 535 | BA0071A826509001001DFFCB /* Environment+Window.swift in Sources */, 536 | BACEC6CF264FF5030070D31B /* DatabaseEntity.swift in Sources */, 537 | BACEC6D0264FF5030070D31B /* DocumentReference+Combine.swift in Sources */, 538 | BAD32C4D264FDDAB00DD7824 /* AppDelegate.swift in Sources */, 539 | BACEC6BD264FF4AC0070D31B /* ContentView.swift in Sources */, 540 | BA00719F2650886C001DFFCB /* OAuth.swift in Sources */, 541 | BA00720226512D63001DFFCB /* ErrorLogger.swift in Sources */, 542 | BA0071FC26512C9C001DFFCB /* Credential.swift in Sources */, 543 | BACEC6DB264FF54B0070D31B /* User.swift in Sources */, 544 | BACEC6CE264FF5030070D31B /* DatabasePathBuilder.swift in Sources */, 545 | BA0B34C6264FA4F100774AA0 /* RootView.swift in Sources */, 546 | BACEC6D1264FF5030070D31B /* CollectionReference+Combine.swift in Sources */, 547 | 20F7E11A26820EA500F0024A /* NotionWebViewPage.swift in Sources */, 548 | BACEC6DA264FF54B0070D31B /* Me.swift in Sources */, 549 | 20F7E1182681FDF400F0024A /* Notion.Object.Page+Extension.swift in Sources */, 550 | BAD32BE3264FC50B00DD7824 /* Secret.swift in Sources */, 551 | 20F7E1162681F65A00F0024A /* NotionWebView.swift in Sources */, 552 | BA0B34C4264FA4F100774AA0 /* TransNotionApp.swift in Sources */, 553 | 20F7E1222682CC5B00F0024A /* Toggle.swift in Sources */, 554 | BACEC6D3264FF5030070D31B /* Auth.swift in Sources */, 555 | BA00722C2651C9E6001DFFCB /* NotionPagesView.swift in Sources */, 556 | ); 557 | runOnlyForDeploymentPostprocessing = 0; 558 | }; 559 | BA0B34CD264FA4F800774AA0 /* Sources */ = { 560 | isa = PBXSourcesBuildPhase; 561 | buildActionMask = 2147483647; 562 | files = ( 563 | BA0B34D6264FA4F800774AA0 /* TransNotionTests.swift in Sources */, 564 | ); 565 | runOnlyForDeploymentPostprocessing = 0; 566 | }; 567 | BA0B34D8264FA4F800774AA0 /* Sources */ = { 568 | isa = PBXSourcesBuildPhase; 569 | buildActionMask = 2147483647; 570 | files = ( 571 | BA0B34E1264FA4F800774AA0 /* TransNotionUITests.swift in Sources */, 572 | ); 573 | runOnlyForDeploymentPostprocessing = 0; 574 | }; 575 | /* End PBXSourcesBuildPhase section */ 576 | 577 | /* Begin PBXTargetDependency section */ 578 | BA0B34D3264FA4F800774AA0 /* PBXTargetDependency */ = { 579 | isa = PBXTargetDependency; 580 | target = BA0B34BF264FA4F100774AA0 /* TransNotion */; 581 | targetProxy = BA0B34D2264FA4F800774AA0 /* PBXContainerItemProxy */; 582 | }; 583 | BA0B34DE264FA4F800774AA0 /* PBXTargetDependency */ = { 584 | isa = PBXTargetDependency; 585 | target = BA0B34BF264FA4F100774AA0 /* TransNotion */; 586 | targetProxy = BA0B34DD264FA4F800774AA0 /* PBXContainerItemProxy */; 587 | }; 588 | /* End PBXTargetDependency section */ 589 | 590 | /* Begin PBXVariantGroup section */ 591 | BACEC6F1264FF9430070D31B /* Localizable.strings */ = { 592 | isa = PBXVariantGroup; 593 | children = ( 594 | BACEC6F0264FF9430070D31B /* en */, 595 | BACEC6F5264FF9690070D31B /* ja */, 596 | ); 597 | name = Localizable.strings; 598 | sourceTree = ""; 599 | }; 600 | /* End PBXVariantGroup section */ 601 | 602 | /* Begin XCBuildConfiguration section */ 603 | BA0B34E3264FA4F800774AA0 /* Debug */ = { 604 | isa = XCBuildConfiguration; 605 | baseConfigurationReference = BA00718926508564001DFFCB /* Debug.xcconfig */; 606 | buildSettings = { 607 | ALWAYS_SEARCH_USER_PATHS = NO; 608 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 609 | CLANG_ANALYZER_NONNULL = YES; 610 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 611 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 612 | CLANG_CXX_LIBRARY = "libc++"; 613 | CLANG_ENABLE_MODULES = YES; 614 | CLANG_ENABLE_OBJC_ARC = YES; 615 | CLANG_ENABLE_OBJC_WEAK = YES; 616 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 617 | CLANG_WARN_BOOL_CONVERSION = YES; 618 | CLANG_WARN_COMMA = YES; 619 | CLANG_WARN_CONSTANT_CONVERSION = YES; 620 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 621 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 622 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 623 | CLANG_WARN_EMPTY_BODY = YES; 624 | CLANG_WARN_ENUM_CONVERSION = YES; 625 | CLANG_WARN_INFINITE_RECURSION = YES; 626 | CLANG_WARN_INT_CONVERSION = YES; 627 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 628 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 629 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 630 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 631 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 632 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 633 | CLANG_WARN_STRICT_PROTOTYPES = YES; 634 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 635 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 636 | CLANG_WARN_UNREACHABLE_CODE = YES; 637 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 638 | COPY_PHASE_STRIP = NO; 639 | DEBUG_INFORMATION_FORMAT = dwarf; 640 | ENABLE_STRICT_OBJC_MSGSEND = YES; 641 | ENABLE_TESTABILITY = YES; 642 | GCC_C_LANGUAGE_STANDARD = gnu11; 643 | GCC_DYNAMIC_NO_PIC = NO; 644 | GCC_NO_COMMON_BLOCKS = YES; 645 | GCC_OPTIMIZATION_LEVEL = 0; 646 | GCC_PREPROCESSOR_DEFINITIONS = ( 647 | "DEBUG=1", 648 | "$(inherited)", 649 | ); 650 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 651 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 652 | GCC_WARN_UNDECLARED_SELECTOR = YES; 653 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 654 | GCC_WARN_UNUSED_FUNCTION = YES; 655 | GCC_WARN_UNUSED_VARIABLE = YES; 656 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 657 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 658 | MTL_FAST_MATH = YES; 659 | ONLY_ACTIVE_ARCH = YES; 660 | SDKROOT = iphoneos; 661 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 662 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 663 | }; 664 | name = Debug; 665 | }; 666 | BA0B34E4264FA4F800774AA0 /* Release */ = { 667 | isa = XCBuildConfiguration; 668 | baseConfigurationReference = BA00718A26508564001DFFCB /* Release.xcconfig */; 669 | buildSettings = { 670 | ALWAYS_SEARCH_USER_PATHS = NO; 671 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 672 | CLANG_ANALYZER_NONNULL = YES; 673 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 674 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 675 | CLANG_CXX_LIBRARY = "libc++"; 676 | CLANG_ENABLE_MODULES = YES; 677 | CLANG_ENABLE_OBJC_ARC = YES; 678 | CLANG_ENABLE_OBJC_WEAK = YES; 679 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 680 | CLANG_WARN_BOOL_CONVERSION = YES; 681 | CLANG_WARN_COMMA = YES; 682 | CLANG_WARN_CONSTANT_CONVERSION = YES; 683 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 684 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 685 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 686 | CLANG_WARN_EMPTY_BODY = YES; 687 | CLANG_WARN_ENUM_CONVERSION = YES; 688 | CLANG_WARN_INFINITE_RECURSION = YES; 689 | CLANG_WARN_INT_CONVERSION = YES; 690 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 691 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 692 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 693 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 694 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 695 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 696 | CLANG_WARN_STRICT_PROTOTYPES = YES; 697 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 698 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 699 | CLANG_WARN_UNREACHABLE_CODE = YES; 700 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 701 | COPY_PHASE_STRIP = NO; 702 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 703 | ENABLE_NS_ASSERTIONS = NO; 704 | ENABLE_STRICT_OBJC_MSGSEND = YES; 705 | GCC_C_LANGUAGE_STANDARD = gnu11; 706 | GCC_NO_COMMON_BLOCKS = YES; 707 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 708 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 709 | GCC_WARN_UNDECLARED_SELECTOR = YES; 710 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 711 | GCC_WARN_UNUSED_FUNCTION = YES; 712 | GCC_WARN_UNUSED_VARIABLE = YES; 713 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 714 | MTL_ENABLE_DEBUG_INFO = NO; 715 | MTL_FAST_MATH = YES; 716 | SDKROOT = iphoneos; 717 | SWIFT_COMPILATION_MODE = wholemodule; 718 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 719 | VALIDATE_PRODUCT = YES; 720 | }; 721 | name = Release; 722 | }; 723 | BA0B34E6264FA4F800774AA0 /* Debug */ = { 724 | isa = XCBuildConfiguration; 725 | buildSettings = { 726 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 727 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 728 | CODE_SIGN_STYLE = Automatic; 729 | DEVELOPMENT_ASSET_PATHS = "\"TransNotion/Preview Content\""; 730 | DEVELOPMENT_TEAM = TQPN82UBBY; 731 | ENABLE_PREVIEWS = YES; 732 | INFOPLIST_FILE = TransNotion/Info.plist; 733 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 734 | LD_RUNPATH_SEARCH_PATHS = ( 735 | "$(inherited)", 736 | "@executable_path/Frameworks", 737 | ); 738 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion; 739 | PRODUCT_NAME = "$(TARGET_NAME)"; 740 | SWIFT_VERSION = 5.0; 741 | TARGETED_DEVICE_FAMILY = "1,2"; 742 | }; 743 | name = Debug; 744 | }; 745 | BA0B34E7264FA4F800774AA0 /* Release */ = { 746 | isa = XCBuildConfiguration; 747 | buildSettings = { 748 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 749 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 750 | CODE_SIGN_STYLE = Automatic; 751 | DEVELOPMENT_ASSET_PATHS = "\"TransNotion/Preview Content\""; 752 | DEVELOPMENT_TEAM = TQPN82UBBY; 753 | ENABLE_PREVIEWS = YES; 754 | INFOPLIST_FILE = TransNotion/Info.plist; 755 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 756 | LD_RUNPATH_SEARCH_PATHS = ( 757 | "$(inherited)", 758 | "@executable_path/Frameworks", 759 | ); 760 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion; 761 | PRODUCT_NAME = "$(TARGET_NAME)"; 762 | SWIFT_VERSION = 5.0; 763 | TARGETED_DEVICE_FAMILY = "1,2"; 764 | }; 765 | name = Release; 766 | }; 767 | BA0B34E9264FA4F800774AA0 /* Debug */ = { 768 | isa = XCBuildConfiguration; 769 | buildSettings = { 770 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 771 | BUNDLE_LOADER = "$(TEST_HOST)"; 772 | CODE_SIGN_STYLE = Automatic; 773 | DEVELOPMENT_TEAM = TQPN82UBBY; 774 | INFOPLIST_FILE = TransNotionTests/Info.plist; 775 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 776 | LD_RUNPATH_SEARCH_PATHS = ( 777 | "$(inherited)", 778 | "@executable_path/Frameworks", 779 | "@loader_path/Frameworks", 780 | ); 781 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion.TransNotionTests; 782 | PRODUCT_NAME = "$(TARGET_NAME)"; 783 | SWIFT_VERSION = 5.0; 784 | TARGETED_DEVICE_FAMILY = "1,2"; 785 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TransNotion.app/TransNotion"; 786 | }; 787 | name = Debug; 788 | }; 789 | BA0B34EA264FA4F800774AA0 /* Release */ = { 790 | isa = XCBuildConfiguration; 791 | buildSettings = { 792 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 793 | BUNDLE_LOADER = "$(TEST_HOST)"; 794 | CODE_SIGN_STYLE = Automatic; 795 | DEVELOPMENT_TEAM = TQPN82UBBY; 796 | INFOPLIST_FILE = TransNotionTests/Info.plist; 797 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 798 | LD_RUNPATH_SEARCH_PATHS = ( 799 | "$(inherited)", 800 | "@executable_path/Frameworks", 801 | "@loader_path/Frameworks", 802 | ); 803 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion.TransNotionTests; 804 | PRODUCT_NAME = "$(TARGET_NAME)"; 805 | SWIFT_VERSION = 5.0; 806 | TARGETED_DEVICE_FAMILY = "1,2"; 807 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TransNotion.app/TransNotion"; 808 | }; 809 | name = Release; 810 | }; 811 | BA0B34EC264FA4F800774AA0 /* Debug */ = { 812 | isa = XCBuildConfiguration; 813 | buildSettings = { 814 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 815 | CODE_SIGN_STYLE = Automatic; 816 | DEVELOPMENT_TEAM = TQPN82UBBY; 817 | INFOPLIST_FILE = TransNotionUITests/Info.plist; 818 | LD_RUNPATH_SEARCH_PATHS = ( 819 | "$(inherited)", 820 | "@executable_path/Frameworks", 821 | "@loader_path/Frameworks", 822 | ); 823 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion.TransNotionUITests; 824 | PRODUCT_NAME = "$(TARGET_NAME)"; 825 | SWIFT_VERSION = 5.0; 826 | TARGETED_DEVICE_FAMILY = "1,2"; 827 | TEST_TARGET_NAME = TransNotion; 828 | }; 829 | name = Debug; 830 | }; 831 | BA0B34ED264FA4F800774AA0 /* Release */ = { 832 | isa = XCBuildConfiguration; 833 | buildSettings = { 834 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 835 | CODE_SIGN_STYLE = Automatic; 836 | DEVELOPMENT_TEAM = TQPN82UBBY; 837 | INFOPLIST_FILE = TransNotionUITests/Info.plist; 838 | LD_RUNPATH_SEARCH_PATHS = ( 839 | "$(inherited)", 840 | "@executable_path/Frameworks", 841 | "@loader_path/Frameworks", 842 | ); 843 | PRODUCT_BUNDLE_IDENTIFIER = com.bannzai.transnotion.TransNotionUITests; 844 | PRODUCT_NAME = "$(TARGET_NAME)"; 845 | SWIFT_VERSION = 5.0; 846 | TARGETED_DEVICE_FAMILY = "1,2"; 847 | TEST_TARGET_NAME = TransNotion; 848 | }; 849 | name = Release; 850 | }; 851 | /* End XCBuildConfiguration section */ 852 | 853 | /* Begin XCConfigurationList section */ 854 | BA0B34BB264FA4F100774AA0 /* Build configuration list for PBXProject "TransNotion" */ = { 855 | isa = XCConfigurationList; 856 | buildConfigurations = ( 857 | BA0B34E3264FA4F800774AA0 /* Debug */, 858 | BA0B34E4264FA4F800774AA0 /* Release */, 859 | ); 860 | defaultConfigurationIsVisible = 0; 861 | defaultConfigurationName = Release; 862 | }; 863 | BA0B34E5264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotion" */ = { 864 | isa = XCConfigurationList; 865 | buildConfigurations = ( 866 | BA0B34E6264FA4F800774AA0 /* Debug */, 867 | BA0B34E7264FA4F800774AA0 /* Release */, 868 | ); 869 | defaultConfigurationIsVisible = 0; 870 | defaultConfigurationName = Release; 871 | }; 872 | BA0B34E8264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotionTests" */ = { 873 | isa = XCConfigurationList; 874 | buildConfigurations = ( 875 | BA0B34E9264FA4F800774AA0 /* Debug */, 876 | BA0B34EA264FA4F800774AA0 /* Release */, 877 | ); 878 | defaultConfigurationIsVisible = 0; 879 | defaultConfigurationName = Release; 880 | }; 881 | BA0B34EB264FA4F800774AA0 /* Build configuration list for PBXNativeTarget "TransNotionUITests" */ = { 882 | isa = XCConfigurationList; 883 | buildConfigurations = ( 884 | BA0B34EC264FA4F800774AA0 /* Debug */, 885 | BA0B34ED264FA4F800774AA0 /* Release */, 886 | ); 887 | defaultConfigurationIsVisible = 0; 888 | defaultConfigurationName = Release; 889 | }; 890 | /* End XCConfigurationList section */ 891 | 892 | /* Begin XCRemoteSwiftPackageReference section */ 893 | BA00721B2651C99C001DFFCB /* XCRemoteSwiftPackageReference "notion" */ = { 894 | isa = XCRemoteSwiftPackageReference; 895 | repositoryURL = "git@github.com:noppefoxwolf/notion.git"; 896 | requirement = { 897 | kind = upToNextMajorVersion; 898 | minimumVersion = 0.1.1; 899 | }; 900 | }; 901 | BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { 902 | isa = XCRemoteSwiftPackageReference; 903 | repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; 904 | requirement = { 905 | kind = upToNextMajorVersion; 906 | minimumVersion = 8.0.0; 907 | }; 908 | }; 909 | /* End XCRemoteSwiftPackageReference section */ 910 | 911 | /* Begin XCSwiftPackageProductDependency section */ 912 | BA00721C2651C99C001DFFCB /* notion */ = { 913 | isa = XCSwiftPackageProductDependency; 914 | package = BA00721B2651C99C001DFFCB /* XCRemoteSwiftPackageReference "notion" */; 915 | productName = notion; 916 | }; 917 | BAD32C3E264FCE5C00DD7824 /* FirebaseFirestoreSwift-Beta */ = { 918 | isa = XCSwiftPackageProductDependency; 919 | package = BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 920 | productName = "FirebaseFirestoreSwift-Beta"; 921 | }; 922 | BAD32C40264FCE5C00DD7824 /* FirebaseCrashlytics */ = { 923 | isa = XCSwiftPackageProductDependency; 924 | package = BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 925 | productName = FirebaseCrashlytics; 926 | }; 927 | BAD32C42264FCE5C00DD7824 /* FirebaseAuth */ = { 928 | isa = XCSwiftPackageProductDependency; 929 | package = BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 930 | productName = FirebaseAuth; 931 | }; 932 | BAD32C44264FCE5C00DD7824 /* FirebaseAnalyticsSwift-Beta */ = { 933 | isa = XCSwiftPackageProductDependency; 934 | package = BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 935 | productName = "FirebaseAnalyticsSwift-Beta"; 936 | }; 937 | BAD32C46264FCE5C00DD7824 /* FirebaseFirestore */ = { 938 | isa = XCSwiftPackageProductDependency; 939 | package = BAD32C3D264FCE5C00DD7824 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; 940 | productName = FirebaseFirestore; 941 | }; 942 | /* End XCSwiftPackageProductDependency section */ 943 | }; 944 | rootObject = BA0B34B8264FA4F100774AA0 /* Project object */; 945 | } 946 | --------------------------------------------------------------------------------