Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwiftUI and no old AppDelegate #917

Closed
Volodymyr-13 opened this issue Mar 6, 2024 · 5 comments
Closed

SwiftUI and no old AppDelegate #917

Volodymyr-13 opened this issue Mar 6, 2024 · 5 comments
Assignees
Labels

Comments

@Volodymyr-13
Copy link

We are currently using the latest iOS 16 and exclusively SwiftUI without an AppDelegate. How can we integrate Box into our iOS app, and why are there no updated samples for utilizing the latest technologies? We are considering integrating Box alongside Google Drive and Dropbox, but upon examining Box, it appears outdated and lacking support. Additionally, it seems that VisionOS has been neglected and remains unsupported even after its full release.

This raises the question: do we really need to integrate Box? I believe it's essential for Box developers to provide maximum compatibility with the latest technologies in their SDK to promote their product. This would allow other developers to easily integrate Box into their iOS apps and potentially include advertisements within the app. Imagine a scenario where a user downloads our app and sees options for Google Drive, Dropbox, and Box, which they may not have previously considered. With such an outdated SDK, however, I fail to see the rationale for integrating Box into our app.


Wow, this form is incredibly long! Do you think anyone will actually take the time to fill it all out?

- [ ] I have checked that the [SDK documentation][sdk-docs] doesn't solve my issue.
- [ ] I have checked that the [API documentation][api-docs] doesn't solve my issue.
- [ ] I have searched the [Box Developer Forums][dev-forums] and my issue isn't already reported (or if it has been reported, I have attached a link to it, for reference).
- [ ] I have searched [Issues in this repo][github-repo] and my issue isn't already reported.

### Description of the Issue
<!-- Replace this text with a description of what problem you're having. -->
<!-- Please include as much detail as possible to help us troubleshoot! -->
<!-- If it isn't obvious, please include how the behavior you expect differs from what actually happened. -->
<!-- This is really important so we know how to start troubleshooting your issue. -->

### Steps to Reproduce
<!-- Please include detailed steps to reproduce the issue you're seeing, if possible. -->
<!-- If you don't have a reproducible error, please make sure that you give us as much detail -->
<!-- as you can about what your application was doing when the error occurred. -->
<!-- Good steps to reproduce the problem help speed up debugging for us and gets your issue resolved sooner! -->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

### Expected Behavior
<!-- What did you expect to happen? -->

### Error Message, Including Stack Trace
<!-- Share the full error output you're seeing, if applicable. -->
<!-- Please include the full stack trace to help us identify where the error is happening. -->

### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->

### Versions Used
iOS SDK: <!-- Replace with the version of the iOS SDK you're using. -->
iOS Preview SDK: <!-- Replace with the version of the iOS Preview SDK you're using, if applicable. -->
iOS: <!-- Replace with the version of iOS your application is running on. -->
Xcode: <!-- Replace with the version of Xcode your application is developed in. -->

[sdk-docs]: ./doc
[api-docs]: https://developer.box.com/docs
[dev-forums]: https://community.box.com/t5/Platform-and-Development-Forum/bd-p/DeveloperForum
[github-repo]: https://github.com/box/box-ios-sdk/search?type=Issues

@arjankowski
Copy link
Contributor

arjankowski commented Mar 7, 2024

Hi @Volodymyr-13,
We apologize for the inconvenience this situation may have caused. Rest assured, we are currently working intensively on a new, revamped SDK that addresses all of the issues mentioned. We plan to release it soon in beta, where all of the concerns raised here will be addressed 🚀

Furthermore, I want to say that support for VisionOS has already been added in the latest pull request, and we will be releasing it shortly.

Regarding the integration itself, I have created a sample application written in SwiftUI where I integrated it with our SDK. It looks something like the following:

import SwiftUI
import BoxSDK

@MainActor class ItemsViewModel: ObservableObject {
    let sdk = BoxSDK(clientId: "<YOUR_CCG_CLIETN_ID>", clientSecret: "<YOUR_CCG_CLIENT_SECRET>")
    var client: BoxClient?
    
    @Published var allItems = [FolderItem]()
    
    func initalizeClient() async  {
        client =  try? await sdk.getCCGClientForUser(userId: "<YOUR_USER_ID>")
    }
    
    func getAllItems() async {
        guard let client else {
            fatalError("No client")
        }
        
        let iterator = client.folders.listItems(folderId: "0")
        
        do {
            repeat {
                let items = try await iterator.next()
                allItems.append(contentsOf: items.entries)
            } while true
        }
        catch let error as BoxSDKError where error.message == .endOfList {
            print("The end of the list has been reached")
        }
        catch {
            print("An error occurred: \(error)")
        }
    }
}

extension FolderItem: Identifiable {
    public var id: ObjectIdentifier {
        switch self {
        case let .file(file):
            return ObjectIdentifier(file)
        case let .folder(folder):
            return ObjectIdentifier(folder)
        case let .webLink(webLink):
            return ObjectIdentifier(webLink)
        }
    }
    
    public var name: String {
        switch self {
        case let .file(file):
            return "file \(file.name ?? "N/A")"
        case let .folder(folder):
            return "folder \(folder.name ?? "N/A")"
        case let .webLink(webLink):
            return "weblink \(webLink.name ?? "N/A")"
        }
    }
}

@MainActor class DetailsViewModel: ObservableObject {
    let client: BoxClient
    let folderItem: FolderItem
    
    @Published var loading: Bool = true
    var details: [String: String] = [:]
    
    init(client: BoxClient, folderItem: FolderItem) {
        self.client = client
        self.folderItem = folderItem
    }
    
    
    func getDetails() async {
        switch folderItem {
        case .folder(let folder):
            let fetchedFolder =  try? await AsyncHelper.asyncifyCallback { (callback: @escaping Callback<Folder>) in
                client.folders.get(folderId: folder.id, fields: ["name","created_by"], completion: callback)
            }
            
            if let fetchedFolder {
                details["name"] = fetchedFolder.name
                details["created by"] = fetchedFolder.createdBy?.name
            }
        case .file(let file):
            let fetchedFile =  try? await AsyncHelper.asyncifyCallback { (callback: @escaping Callback<File>) in
                client.files.get(fileId: file.id, fields: ["name","created_by"], completion: callback)
            }
            
            if let fetchedFile {
                details["name"] = fetchedFile.name
                details["created by"] = fetchedFile.createdBy?.name
            }
        case .webLink(let webLink):
            let fetchedWebLink =  try? await AsyncHelper.asyncifyCallback { (callback: @escaping Callback<WebLink>) in
                client.webLinks.get(webLinkId: webLink.id, fields: ["name","created_by","shared_link"], completion: callback)
            }
            
            if let fetchedWebLink {
                details["name"] = fetchedWebLink.name
                details["created by"] = fetchedWebLink.createdBy?.name
                if let sharedLink = fetchedWebLink.sharedLink {
                    details["shared lnik"] = sharedLink.downloadURL?.absoluteString
                }
            }
        }
        
        loading = false
    }
}

struct DetailsView: View {
    @StateObject var vm: DetailsViewModel
    
    var body: some View {
        VStack {
            if vm.loading {
                ProgressView("Loading…")
                    .scaleEffect(3)
                    .font(.system(size:8))
            } else {
                List{
                    ForEach(vm.details.sorted(by: >), id: \.key) { key,value in
                        Section(header: Text(key)) {
                            Text(value)
                        }
                    }
                }
            }
        }.task {
            await vm.getDetails()
        }
    }
}

struct ContentView: View {
    @StateObject var vm = ItemsViewModel()
    
    var body: some View {
        NavigationStack {
            List(vm.allItems) { item in
                NavigationLink(destination: DetailsView(vm: DetailsViewModel(client: vm.client!, folderItem: item))){
                    Text(item.name).font(.title)
                }
            }.task {
                await vm.initalizeClient()
                await vm.getAllItems()
            }
        }
    }
}

#Preview {
    ContentView()
}

Of course, how the integration will ultimately look depends on the app architecture. In my example, I used the simplest form of integration, and CCG authentication. Additionally, for convenience, I utilized the added mechanism of wrapping our API in async/await using AsyncHelper.asyncifyCallback. I hope this helps!

In simple terms, we think picking our product, especially with the upcoming improvements we're making to our SDK, will give you the best way to add cloud storage to your iOS app. Thanks for thinking about it, and we're here to help you with your app development.

Regards,
Artur

@Volodymyr-13
Copy link
Author

Hi Artur,

Thanks for providing the update on the Box SDK improvements and the sample application integration. It's great to hear that you're actively addressing the issues raised and adding support for VisionOS.

However, I have a concern regarding the public visibility of certain fields, such as the accessToken property and other.

When the SDK returns an object, we need it properties(our "fixes"):
Screenshot 1

Could you please fix property and make it public?

Thanks for your attention to this matter.

@Volodymyr-13
Copy link
Author

Furthermore, we've encountered difficulty in locating any logic to handle expired tokens. Is there any existing code to address this issue, or is manual implementation our only option? We've observed that Dropbox excels in this aspect, offering robust solutions for handling expired tokens.

@arjankowski
Copy link
Contributor

Hi @Volodymyr-13,
regarding making fields from TokenInfo available, please see if this solution in this PR is OK for you.

As for handling token expiration, the SDK provides this functionality, and there's no need to do it manually.
Before making a request, the SDK checks if the token is valid:

unwrappedTokenInfo.expiresAt.timeIntervalSinceNow > configuration.tokenRefreshThreshold

If so, the request is made; otherwise, it is automatically refreshed.

The parameter tokenRefreshThreshold itself is configurable, which you can read about here.

Hope this will help you!
Artur

@Volodymyr-13
Copy link
Author

Hey, yep, that PR should be approved!
Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants