SKCloudServiceController.requestAuthorization gets stuck upon authorization - objective-c

I am referring to the following SKCloudServiceController.requestAuthorization method. Once the status is authorized, I would like to update #State var showStart = false so that the view can be pushed to the next one.
if (showStart) {
NavigationLink(destination: Main(), isActive: $showStart) {
EmptyView()
}.hidden()
}
SKCloudServiceController.requestAuthorization { (status) in
if status == .authorized {
print(AppleMusicAPI().fetchStorefrontID())
showStart = true
}
}
But after this runs and the status is authorized, the app freezes and does not change showStart.

Fixed by implementing the following function instead. It allows SKCloudServiceController.requestAuthorization to finish and then upon completion, set showStart to true.
func requestAccess(_ completion: #escaping(Bool) -> Void) {
SKCloudServiceController.requestAuthorization { (status) in
switch status {
case .authorized:
completion(true)
case .denied, .notDetermined, .restricted:
completion(false)
#unknown default:
completion(false)
}
}
}
requestAccess { (true) in
showStart = true
}

Related

iOS16 - How to navigate through hidden links with an optional value?

I previously used an optional value to click a hidden link to navigate in my app. Something like this example in Hacking With Swift:
#State private var selection: String? = nil
var body: some View {
....
NavigationLink(destination: Text("View A"), tag: "A", selection: $selection) { EmptyView() }
NavigationLink(destination: Text("View B"), tag: "B", selection: $selection) { EmptyView() }
Button("Tap to show A") {
selection = "A"
}
Button("Tap to show B") {
selection = "B"
}
With iOS 16 this is deprecated. I am currently setting an optional value and when it's not nil I want a link to open. I can't figure out how to do it with the new NavigationLink/Value/Destination combination. Has anyone else figured out how to do it?
I created a new projects and switched ContentView to the following:
private enum Destinations: Hashable {
case empty
case general
case myQuestionView(String)
}
struct ContentView: View {
#State private var selection: Destinations?
#State private var mySelectedString: String?
var body: some View {
NavigationSplitView {
List(selection: $selection) {
NavigationLink(value: Destinations.general) {
Text("Example")
}
if mySelectedString != nil {
NavigationLink(value: Destinations.myQuestionView(mySelectedString!)) {
Text("String: \(mySelectedString ?? "no name")")
}
}
Button(action: {
mySelectedString = "A Name"
}, label: {
Text("Set the value")
})
}
} detail: {
NavigationStack {
switch selection ?? .empty {
case .empty: Text("Please select an option to continue.")
case .general: Text("Result of this option")
case .myQuestionView(let aString): Text("Hello \(aString)")
}
}
}
}
}
Here the Set the value button sets the selectedString which makes the link appear but I can't make it automatically navigate AND, ideally, it would never appear and would navigate when the value is set.
Okay... the answer is super simple and leaving the question and this answer up in case it helps someone else.
You don't need to have an invisible NavigationLink. You just need to set the selection to the new destination: selection = .myQuestionView("this name instead"). Or, if I was using the navigation path NavigationStack(path: $myPath) I'd just append it.
So in this specific example ContentView would now be:
private enum Destinations: Hashable {
case empty
case general
case myQuestionView(String)
}
struct ContentView: View {
#State private var selection: Destinations?
var body: some View {
NavigationSplitView {
List(selection: $selection) {
NavigationLink(value: Destinations.general) {
Text("Example")
}
Button(action: {
selection = .myQuestionView("this name instead")
}, label: {
Text("Set the value")
})
}
} detail: {
NavigationStack {
switch selection ?? .empty {
case .empty: Text("Please select an option to continue.")
case .general: Text("Result of this option")
case .myQuestionView(let aString): Text("Hello \(aString)")
}
}
}
}
}

How to call function on XMLHttpRequest status = true in Vue 2? I get "this.xxxx not a function" error

I have the following code, which works fine except for the "makeToast" function that I'm trying to call when status response is true. I get a "this.makeToast is not a function" error on the console.
This function is working fine if I call it after the XMLHttpRequest code. The data is also not being assigned to the msgForm property. I could not figure out why. The "alert(..." message work fine.
<script>
import ToastMixins from '/src/mixins/ToastMixins'
let config = {
headers: {
}
}
export default {
name: 'ModalDestaque',
mixins: [
ToastMixins
],
methods: {
myFunction() {
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log('onreadystatechange');
console.log('responseText 1', xhr.responseText);
this.loading = false;
if (xhr.status == 200) {
console.log('responseText 2', xhr.responseText);
let responseObj = JSON.parse(xhr.responseText);
console.log('responseObj', responseObj);
if (responseObj.status == true) {
//alert('Ok');
// this is not working:
this.msgForm = "Message success!";
this.makeToast('b-toaster-bottom-right', true, 'success');
} else {
alert('Not ok...');
}
}
}
};
}
}
}
What am I doing wrong?
I've found the solution while reading the docs at W3 Schools.
W3 Schools AJAX XMLHttp - Multiple Callback Functions
Although, I haven't found a working example anywhere.
In my code, at the button click event that triggers the XMLHttpRequest, I've added the function name "callToast" as a variable, so:
#click="onClickSubmit(myValue, myId, myTitle, callToast)"
Then in the script:
<script>
onClickSubmit(amount, id, title, cFunction) {
// stuff
if (xhr.status == 200) {
let responseObj = JSON.parse(xhr.responseText);
if (responseObj.status == true) {
// here I call the callToast function:
cFunction(this);
alert('Ok');
} else {
alert('Not ok...');
}
}
},
callToast() {
this.msgForm = "Message success!";
this.makeToast('b-toaster-bottom-right', true, 'success');
}
</script>

Better way to raise number pad SwiftUI

Upon navigating to a view, I want the number pad to already be raised. Right now I have a solution that works the first time (albeit with a delay) but fails to raise the number pad if the user navigates back a second time. Is there a better way to raise the number pad in SwiftUI (or to have it always up)?
Example Code:
struct ParentView: View {
#FocusState var numberPadFocused: Bool
#State var isActive: Bool = false
var body: some View {
NavigationView {
VStack {
Button {
numberPadFocused = true
isActive = true
print("Called")
} label: {
Text("Navigate")
}
NavigationLink(destination: ChildView(focusState: $numberPadFocused), isActive: $isActive) { Color.white }
}
}
}
}
struct ChildView: View {
#State var text: String = ""
#FocusState.Binding var focusState: Bool
var body: some View {
TextField("Enter Number...", text: $text)
.keyboardType(.numberPad)
.focused($focusState)
}
}

Load more functionality using SwiftUI

i have used ScrollView with HStack, now i need to load more data when user reached scrolling at last.
var items: [Landmark]
i have used array of items which i am appeding in HStack using ForEach
ScrollView(showsHorizontalIndicator: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
}
}
}
What is the best possible solution to manage load more in SwiftUI without using custom action like loadmore button.
It's better to use ForEach and List for this purpose
struct ContentView : View {
#State var textfieldText: String = "String "
private let chunkSize = 10
#State var range: Range<Int> = 0..<1
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("Load more")
}
}
}
func loadMore() {
print("Load more...")
self.range = 0..<self.range.upperBound + self.chunkSize
}
}
In this example each time you press load more it increases range of State property. The same you can do for BindableObject.
If you want to do it automatically probably you should read about PullDownButton(I'm not sure if it works for PullUp)
UPD:
As an option you can download new items by using onAppear modifier on the last cell(it is a static button in this example)
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("")
}
.onAppear {
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
self.loadMore()
}
}
}
}
Keep in mind, that dispatch is necessary, because without it you will have an error saying "Updating table view while table view is updating). Possible you may using another async way to update the data
If you want to keep using List with Data instead of Range, you could implement the next script:
struct ContentView: View {
#State var items: [Landmark]
var body: some View {
List {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
.onAppear {
checkForMore(landmark)
}
}
}
}
func checkForMore(_ item: LandMark) {
guard let item = item else { return }
let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
// function to request more data
getMoreLandMarks()
}
}
}
Probably you should work in a ViewModel and separate the logic from the UI.
Credits to Donny Wals: Complete example

Extjs4, wait for ajax request

I should run multiple ajax requests in one button click, but all requests should wait until the first one is executed. I have tried to put all requests in the success callback of the first one but this gives this error:
TypeError: o is undefined
return o.id;
And just the first request is executed.
This is my code:
if(form1.isValid()) {
form1.submit(me._genFormSubmitAction('my_DB','my_Action', function() {
console.log('form1 success');
//Submit Form2
if(form2.isValid()) {
form2.submit(me._genFormSubmitAction('my_DB','my_Action', function() {
console.log('form2 success');
}));
//Submit Form3
....
_genFormSubmitAction:
_genFormSubmitAction: function(db,action, successCallback) {
var me = this;
return {
clientValidation : true,
url : me.getApplication().apiUrl,
waitMsg : '<p align=right>..الرجاء الإنتظار</p>',
async:false,
params : {
_module: 'administrationcassocial',
_action: action,
_db:db
},
success : function(form, action) {
if(action.result.success == true) {
Ext.callback(successCallback, me);
form.owner.destroy();
} else {
console.log('url=',url);
Ext.Msg.alert(action.result.error, action.result.errormessages.join("\n"));
}
},
failure : function(form, action) {
switch (action.failureType) {
case Ext.form.action.Action.CLIENT_INVALID:
Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
break;
case Ext.form.action.Action.CONNECT_FAILURE:
Ext.Msg.alert('Failure', 'Ajax communication failed');
break;
case Ext.form.action.Action.SERVER_INVALID:
Ext.Msg.alert(action.result.error, action.result.errormessages.join("\n"));
}
}
};
}
This is a scope issue.
The callback of form1.submit happens in the callback own scope, so it has no idea what form2 is.
You can try:
if(form1.isValid()) {
var me = this;
form1.submit(me._genFormSubmitAction('my_DB','my_Action', function() {
console.log('form1 success');
//Submit Form2
if( me.form2.isValid() ) {
form2.submit(me._genFormSubmitAction('my_DB','my_Action', function() {
console.log('form2 success');
}));
}
}));
}
Or the more proper solution in my view:
// Added aScope var
_genFormSubmitAction: function( db,action, aScope, successCallback ) {
var me = this;
return {
// ...
scope: aScope
}
}
Then you call:
form1.submit(me._genFormSubmitAction('my_DB','my_Action', this, function() {
}));