Post

Replies

Boosts

Views

Activity

How to use frameworks with the same name for different scheme
So I have 2 schemes: prod and staging. And both have their own Debug and Release configuration settings (one of which is the bundle ID, eg. prod-Release: com.mycomp.myApp, prod-Debug: com.myComp.myApp.debug, staging-Release: com.myComp.myApp.staging.debug, staging-Debug: com.myComp.myApp.staging.debug). Now, there is a framework that is bound to each bundle ID. The framework is not from a So I need 4 instances of the same framework for each bundle ID. I tried separating the frameworks into folders and just link them whenever I need to run the appropriate scheme. Basically like so: \{PROJECT_ROOT}\Frameworks\MyFramework\prod-Debug\MyFramework.xcframework \{PROJECT_ROOT}\Frameworks\MyFramework\prod-Release\MyFramework.xcframework \{PROJECT_ROOT}\Frameworks\MyFramework\staging-Debug\MyFramework.xcframework \{PROJECT_ROOT}\Frameworks\MyFramework\staging-Release\MyFramework.xcframework I've added a Run script to create the link before Compile Sources step like so: cd "${SRCROOT}/zDefend" if [ "${CONFIGURATION}" = "prod-Debug" ]; then rm -rf ./MyFramework.xcframework ln -s ./prod-Debug/MyFramework.xcframework MyFramework.xcframework elif [ "${CONFIGURATION}" = "prod-Release" ]; then rm -rf ./MyFramework.xcframework ln -s ./prod-Release/MyFramework.xcframework/ MyFramework.xcframework elif [ "${CONFIGURATION}" = "staging-Debug" ]; then rm -rf ./MyFramework.xcframework ln -s ./staging-Debug/MyFramework.xcframework MyFramework.xcframework elif [ "${CONFIGURATION}" = "staging-Release" ]; then rm -rf ./MyFramework.xcframework ln -s ./staging-Release/MyFramework.xcframework/ MyFramework.xcframework fi I only have 1 target, myApp. But in the Build Settings, I set it so that every configurations will create 4 different binaries with 4 different bundle ID like I've mentioned above. I've added the framework by its link and set it to "Embed and Sign". The framework itself is not from Cocoapods, Carthage or SwiftPM. We just need to compile it ourselves into 4 and bind them with their own bundle ID in the code. Then we manually added them into the project. It will throw error if the app's bundle ID is not the same with the bundle ID bound to the framework. This can be compiled just fine. But when in created 2 or more binaries in the simulator, it crashes. So this is possibly a linking issue. How do I solve this? Thank you.
0
0
361
Jun ’24
App crashed with NSInvalidUnarchiveOperationException when run from different target and scheme.
Hi, I'm trying to create a new target duplicated from the main target (cdx_ios) called cdx-ios-dev02. I also made a new scheme called cdx-ios-dev02 It can be built just fine however when I run it, it crashed and it throws this exception: *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (cdx_ios.AuthObject) for key (root) because no class named "cdx_ios.AuthObject" was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target). If the class was renamed, use setClassName:forClass: to add a class translation mapping to NSKeyedUnarchiver' This is the class: class AuthObject: NSObject, NSCoding { var accessT1: String = "" var t1Type: String = "bearer" var refreshT1: String = "" var expiresIn: Int = 0 var scope: String = "" var jti: String = "" init(accessT1: String = "", t1Type: String = "bearer", refreshT1: String = "", expiresIn: Int = 0, scope: String = "", jti: String = "") { self.accessT1 = accessT1 self.t1Type = t1Type self.refreshT1 = refreshT1 self.expiresIn = expiresIn self.scope = scope self.jti = jti } convenience init(dic: [String: Any]) { self.init() mapping(dic) } required convenience init(coder aDecoder: NSCoder) { let t1 = aDecoder.decodeObject(forKey: "accessT1") as? String ?? "" let t1Type = aDecoder.decodeObject(forKey: "t1Type") as? String ?? "" let refreshT1 = aDecoder.decodeObject(forKey: "refreshT1") as? String ?? "" let expiresIn = aDecoder.decodeInteger(forKey: "expiresIn") let scope = aDecoder.decodeObject(forKey: "scope") as? String ?? "" let jti = aDecoder.decodeObject(forKey: "jti") as? String ?? "" self.init( accessT1: t1, t1Type: t1Type, refreshT1: refreshT1, expiresIn: expiresIn, scope: scope, jti: jti ) } func mapping(_ dic: [String: Any]) { accessT1 = ParseUtil.dictionaryValue(dic, "access_token", "") t1Type = ParseUtil.dictionaryValue(dic, "token_type", "bearer") refreshT1 = ParseUtil.dictionaryValue(dic, "refresh_token", "") expiresIn = ParseUtil.dictionaryValue(dic, "expires_in", 0) scope = ParseUtil.dictionaryValue(dic, "scope", "") jti = ParseUtil.dictionaryValue(dic, "jti", "") } func encode(with nsCoder: NSCoder) { nsCoder.encode(accessT1, forKey: "accessT1") nsCoder.encode(t1Type, forKey: "t1Type") nsCoder.encode(refreshT1, forKey: "refreshT1") nsCoder.encode(expiresIn, forKey: "expiresIn") nsCoder.encode(scope, forKey: "scope") nsCoder.encode(jti, forKey: "jti") } } It worked fine on the original target, cdx-ios. Can anybody help me? Thank you.
3
0
653
Jun ’24
xcbuild didn't run tests even though it can be run from XCode
I am trying to do the tests form the terminal and xcpretty the output. Here is the command: xcodebuild -workspace bimb-authenticate-ios.xcworkspace -scheme bimb-authenticate-ios-afx -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 14 Pro,OS=16.4' -enableCodeCoverage YES -derivedDataPath Build/ clean build test | xcpretty -c "--report" "html" "--output" "../Project_report.html" However, the tests are never run. The simulator is not even opened. Here's the last items of the xcbuild logs: ... *compiling, linting and linking logs* 2024-03-27 21:42:37.151 xcodebuild[84540:2237180] [MT] IDETestOperationsObserverDebug: 264.553 elapsed -- Testing started completed. 2024-03-27 21:42:37.152 xcodebuild[84540:2237180] [MT] IDETestOperationsObserverDebug: 0.001 sec, +0.001 sec -- start 2024-03-27 21:42:37.152 xcodebuild[84540:2237180] [MT] IDETestOperationsObserverDebug: 264.554 sec, +264.553 sec -- end ▸ Test Succeeded Can anybody help me? Thanks.
0
0
523
Mar ’24
Push notification is untappable in iPhone X/XS with iOS 16.2
Hi, I'm experiencing a bug that only occur in iPhone X/XS that run iOS 16.2. I couldn't tap the push notification when the app is already active. I need to trigger a feature when the push notification is tapped. When the app is on the background, it worked. But when the app is already active / on the foreground, it didn't. I tried this on an iPhone 8 Plus running iOS 16.2.1 and iPhone 14 and iPhone XR running iOS 17.1.2, but this bug didn't occur (ie. the tap was working and I can call didReceiveResponse callback). Why does this happen? Can anybody help me? Thanks.
1
0
524
Mar ’24
Using the same app group ID with another team?
Hi, Just a quick one. I am working with a client who doesn't share his team's credentials like certificates, mobile provisioning, etc. He even refused to add me as one of the developer in his Apple Dev account. So, I am creating a new scheme for me that will use my own personal team and app ID to build it. While the main app's original scheme is basically unusable since I don't have the credentials to build it. The client still needs it for his CI/CD though. Now, the app has a Notification Service extension that will share UserDefaults via App Group. When I try to create a container with the same group ID as his, it always failed. It seems like we can't use it because it has already been taken by the clent. How do I fix this so I can just change the scheme to switch between the client's and mine? Thanks.
1
0
1.2k
Jan ’24
Cannot build project that has multiple targets with the same settings and phases, but different teams and bundle id
Hi, I'm trying to create some targets of my project with the exact source code, Info.plist, build settings, build phases, extensions, copied files, tests, etc like this: I've also created some schemes so we can toggle the targets by picking the scheme like this: I've set all of these targets with the same value instead of getting it from their $TARGET, $PRODUCT_BUNDLE_ID, etc: Info plist file = bimb-authenticate-ios/Info.plist Project Module Name = bimb_authenticate_ios Bundle Display name = BIMBAuthenticator I've added the targets in the Podfile like so: Podfile But when I tried building it, I;ve got these "Multiple commands" errors: Can you tell me what I did wrong here? Thank you.
0
1
400
Jan ’24
HTML tags in UILabel don't work
Hi, I've got this html text: "<style>* {font-size: 12pt !important;color: #000000 !important;font-family: Montserrat-Regular !important;}</style>Perform the following steps:<br><u>Option 1:</u><br><p>1) Upon receiving a push notification alert, tap on the push notification to launch BIMB Authenticator</p><p>2) Verify the transaction details and choose \"Approve\"</p><p>3) Complete</p><br><u>Option 2:</u><br><ol><p>1) If you didn’t receive push notification, you may launch BIMB Authenticator</p><p>2) Verify the transaction details and choose \"Approve\"</p><p>3) Complete</p>" And I'm trying to show this HTML text properly in a UILabel. This is my codes: String extension to map to NSAttributedString: extension String { func attributedStringFromHTML() -> NSAttributedString? { guard let data = "\(self)" .data(using: .utf8, allowLossyConversion: false) else { Log.error(category: .transaction, message: "Unable to decode data from html string: %@", self) return nil } let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [ .documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue ] if let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) { return attributedString } else { Log.error(category: .transaction, message: "Unable to create attributed string from html string: %@", self) return nil } } } And this is the result: Can you tell me how to fix this? Thanks.
0
0
770
Dec ’23
Custom view animates from bottom - up at first, but not afterwards.
I have this ToastView that will animate by expanding the height from the top of the screen. All the components are created programmatically. This is the code: final class ToastView: UIView { private static let instance = createContainer() private let labelHeightOffset: CGFloat = 32 private let messageLabel: UILabel = { let label = UILabel() label.textAlignment = .center label.numberOfLines = 0 return label }() private var heightConstraint: NSLayoutConstraint! static func show(message: String) { let keyWindow = UIWindow.getKeyWindow() //extension to get the kley window show(on: keyWindow, message: message) } private static func show(on parentView: UIView? = nil, message: String) { guard let parentView = parentView ?? UIViewController.getTopViewController()?.view, instance.superview == nil, let toast = toast else { return } parentView.addSubview(instance, method: .fill) toast.messageLabel.text = message toast.messageLabel.textColor = .red toast.messageLabel.textAlignment = .center toast.messageLabel.sizeToFit() toast.show(for: 3) } private func show(for duration: TimeInterval = 0) { isHidden = false layoutIfNeeded() UIView.animate(withDuration: 0.4, animations: { let labelLineHeight = self.messageLabel.getRect(maxLine: 3).size.height //getRect is an extension func to get the rect based on the content of the label let lineSpaces = CGFloat((self.messageLabel.getLineNumber() - 1) * 2) //getLineNumber is an extension func to get the total number of lines based on the content //Get the height by the content of the label self.heightConstraint.constant = self.labelLineHeight + self.labelHeightOffset + lineSpaces self.setNeedsUpdateConstraints() self.superview?.layoutIfNeeded() }, completion: { _ in self.hide(delay: duration) }) } private func hide(delay: TimeInterval = 0) { UIView.animate(withDuration: 0.4, delay: delay, animations: { self.heightConstraint.constant = 0 self.setNeedsUpdateConstraints() self.superview?.layoutIfNeeded() }, completion: { _ in self.isHidden = true self.messageLabel.text = nil ToastView.instance.removeFromSuperview() }) } } private extension ToastView { static var toast: ToastView? { return instance.subviews[0] as? ToastView } static func createContainer() -> UIView { let container = UIView(frame: .zero) container.backgroundColor = .clear let toast = ToastView(frame: .zero) toast.backgroundColor = .white toast.addCorner(radius: 8) container.addSubview(toast) toast.layoutMessageLabel() toast.layoutToast() return container } func layoutMessageLabel() { addSubview(messageLabel) messageLabel.center = center messageLabel.translatesAutoresizingMaskIntoConstraints = false let constraints = [ centerYAnchor.constraint(equalTo: messageLabel.centerYAnchor), leftAnchor.constraint(equalTo: messageLabel.leftAnchor, constant: 16), rightAnchor.constraint(equalTo: messageLabel.rightAnchor, constant: 16) ] NSLayoutConstraint.activate(constraints) messageLabel.setNeedsUpdateConstraints() layoutIfNeeded() } func layoutToast() { translatesAutoresizingMaskIntoConstraints = false let topConstants: CGFloat = UIWindow.getKeyWindow()?.safeAreaInsets.top ?? 0 + 16 let topConstraint = topAnchor.constraint(equalTo: superview!.topAnchor, constant: topConstants) heightConstraint = heightAnchor.constraint(equalToConstant: 0) let constraints = [ topConstraint, heightConstraint!, leftAnchor.constraint(equalTo: superview!.leftAnchor, constant: 16), superview!.rightAnchor.constraint(equalTo: rightAnchor, constant: 16) ] NSLayoutConstraint.activate(constraints) setNeedsUpdateConstraints() superview!.layoutIfNeeded() } } The code is working fine EXCEPT for the first time it appears. It always animates from the bottom of the screen and rising above ot the top. But if you'll have a look at the code, I only animte the heightConstraint's constant value. Why is this happening? Can you help me fix this? thanks.
1
0
966
Dec ’23
os_log wrapper crashed when using "%s"
I am trying to wrap os_log for logging in my iOS app like so: import Foundation import OSLog enum LogCategory: String, CaseIterable { case viewCycle case tracking case api } struct Log { private static let logs = { return LogCategory.allCases .reduce(into: [LogCategory: OSLog]()) { dict, category in dict[category] = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "BIMB", category: category.rawValue) } }() static func debug(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .debug, args) } static func info(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .info, args) } static func notice(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .default, args) } static func warning(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .default, args) } static func error(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .error, args) } static func critical(category: LogCategory, message: StaticString, _ args: CVarArg...) { logImpl(category: category, message: message, type: .fault, args) } private static func logImpl(category: LogCategory, message: StaticString, type: OSLogType, _ args: CVarArg...) { guard let log = logs[category] else { return } os_log(message, log: log, type: type, args) } } The problem is if I did this: Log.debug(category: .tracking, message: "Device ID: %s.", UIDevice.current.identifierForVendor?.uuidString ?? "unknown") it always crashed with this error: 2023-12-13 12:33:35.173798+0700 bimb-authenticate-ios[62740:928633] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Swift.__SwiftDeferredNSArray UTF8String]: unrecognized selector sent to instance 0x600000dcbbc0' But if I just do it with os_log like this: os_log("Device ID: %s.", log: OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "tracking"), type: .debug, UIDevice.current.identifierForVendor?.uuidString ?? "unknown") it worked fine. Also if I changed %@ in my wrapper instead, it didn't crash, but the idfv is shown inside a pair of brackets like this: Device ID: ( "C0F906C8-CD73-44F6-86A1-A587248680D3" ).` But with os_log it is shown normally like this: Device ID: C0F906C8-CD73-44F6-86A1-A587248680D3. Can you tell me what's wrong here? And how do I fix this? Thanks. NOTE: This is using os_log since the minimum version is iOS 11. I don't know why people advising me with using Logger instead.
1
0
731
Dec ’23
jQuery modal dialog popup not showing in WKWebView
Hi, I have a WKWebview to open the client's website. In the website, there is a "Cancel" button that will open a popup for confirmation. In Safari or other web clients, it works and it will open a popup. But in my iOS app, it didn't. When I ask the web FE developer, he said that he is using jQuery modal dialog for the popup. I tried using this, but doesn't work: class WebsiteViewController: UIViewController { var urlRequest: URLRequest? @IBOutlet weak private var ibWebView: WKWebView! override func viewDidLoad() { super.viewDidLoad() ibWebView.configuration.websiteDataStore = .nonPersistent() ibWebView.configuration.preferences.javaScriptEnabled = true ibWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true ibWebView.navigationDelegate = self ibWebView.uiDelegate = self loadInternetBanking() } private func loadInternetBanking() { guard let urlRequest = urlRequest else { return } ibWebView.load(urlRequest) } } extension InternetBankingViewController: WKUIDelegate { func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { print(":::::::: createWebViewWith!!!!") //- this doesn't get called let popupWebView = WKWebView(frame: view.bounds, configuration: configuration) popupWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight] return popupWebView } func webViewDidClose(_ webView: WKWebView) { } func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo) async { print(":::::::: runJavaScriptAlertPanelWithMessage!!!!") //- this doesn't get called } func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo) async -> Bool { print(":::::::: runJavaScriptConfirmPanelWithMessage!!!!") //- this doesn't get called return true } } The createWebViewWith, runJavaScriptAlertPanelWithMessage, and runJavaScriptConfirmPanelWithMessage callbacks are not being called. Can you help me with this? Thank you.
1
0
694
Nov ’23
Unit test never runs
I am using Xcode 14.3.1 to develop and I have this unit test: @testable import bimb_authenticate_ios import XCTest import RxSwift import RxBlocking final class BindingApiClientTests: XCTestCase { private var disposeBag = DisposeBag() override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. disposeBag = DisposeBag() } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } func testPostInternetBankingCredentials_WhenSuccess() throws { XCTAssertEqual(1, 1) // let expectedParams = getExpectedIBCredsParams() // let expectedResponseModel = getExpectedIBCredentialsResponseModel() // let expectedData = try expectedResponseModel.jsonData() // Encodable to Data from JSON string // let expectedOutput = Single.just(expectedData) // let apiDataSource = MockDataSource(postOutputEvent: expectedOutput) // let sut = BindingApiClient(apiDataSource: apiDataSource) // // let expectation = expectation(description: "Expect Success!") // var actualResponseModel: IBCredentialsResponseModel? = try sut // .postInternetBankingCredentials(with: expectedParams) // .debug() // .toBlocking() // .first() // //// sut.postInternetBankingCredentials(with: expectedParams) //// .subscribe(onSuccess: { //// actualResponseModel = $0 //// expectation.fulfill() //// }) //// .disposed(by: disposeBag) //// //// wait(for: [expectation], timeout: 0.1) // XCTAssertEqual(expectedResponseModel, // actualResponseModel) // XCTAssertEqual(expectedParams.toJSONString(), // apiDataSource.postParamsInput?.toJSONString()) // XCTAssertTrue(apiDataSource.isPostCalled) } }` But it never run even though it only had one simple line, XCTAssertEqual(1, 1). It just started and will be processing ad infinitum. There is no log whatsoever except for the logs for successful compilations of pods and some swiftlint warnings. Can anybody help me? Thanks.
2
0
637
Nov ’23