What's the correct way to implement default properties when doing default implementation of protocols in Swift? - protocols

I am truly fascinated by the concept of Protocol-Oriented Programming in Swift, and because of that, I'm moving an old project I created last year (which was originally an OOP framework) to POP.
At this stage, the problems I'm having could be because I'm either understanding POP incorrectly, or the Swift 2.0 Betas don't have everything to create a truly Protocol-Oriented framework (not very likely - If anything I might be misunderstanding certain aspects of POP).
Protocol-Oriented Programming is a whole new programming paradigm introduced to the world less than a month ago, so there is not much written content about it (the only tutorial I found on the topic doesn't address the problem I'm having, and the WWDC video didn't either).
Anyway, to the point: I am either doing something wrong here, or one of the downsides of Protocol-Oriented Programming is that you are bound to repeat a lot of code. Case in point:
I have the following protocol that has many properties and also complies with the Equatable protocol:
protocol MediaType : Equatable {
/// MARK: - Properties
/// The ID of the media, as assigned by MyAnimeList.
var ID: Int { get }
/// The title of the media.
var title: String { get }
/// Other titles by which this anime may be commonly known (only available in details requests).
var otherTitles: (synonyms: [String], english: [String], japanese: [String])? { get }
/// The global ranking of the title (only available in anime details requests).
var rank: Int? { get }
/// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
var popularityRank: Int? { get }
/// URL to a representative image of the title. Usually a "cover" image.
var imageURL: String { get }
/// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
var adaptations: Relationships { get }
/// The user's rating of the media.
var memberScore: Float { get }
/// Number of MyAnimeList members that that added the title to their list (only available in details requests).
var membersCount: Int? { get }
/// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
var favoritedCount: Int? { get }
/// A short HTML-formatted description of the media.
var synopsis: String { get }
/// A list of genres for this title (only available in details requests).
var genres: [String]? { get }
/// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
var tags: [String] { get }
}
In the original version of my framework, this protocol was a class called Media, and two other classes inherited from it. So they got all those properties for free.
But it doesn't look like I can give my objects that comply with that protocol a default implementation (namely, getters) for those properties?
The first thing I tried, which is to simply give the protocol to my struct declaration, failed, which was to be expected as I wasn't providing any implementation for the properties:
struct Anime : MediaType {
/// MARK: - MediaType
}
/// Compares two Anime_ objects. Two Anime_ objects are considered equal when they have the same ID and title.
func ==(lhs: Anime, rhs: Anime) -> Bool {
return (lhs.ID == rhs.ID) && (lhs.title == rhs.title)
}
This fails with:
Type 'Anime' does not conform to protocol 'MediaType'
My next attempt was to write an extension for MediaType, and throw the properties there:
extension MediaType {
/// The ID of the media, as assigned by MyAnimeList.
let ID: Int
/// The title of the media.
let title: String
/// Other titles by which this anime may be commonly known (only available in details requests).
let otherTitles: (synonyms: [String], english: [String], japanese: [String])?
/// The global ranking of the title (only available in anime details requests).
let rank: Int?
/// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
let popularityRank: Int?
/// URL to a representative image of the title. Usually a "cover" image.
let imageURL: String
/// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
let adaptations: Relationships
/// The user's rating of the media.
let memberScore: Float
/// Number of MyAnimeList members that that added the title to their list (only available in details requests).
let membersCount: Int?
/// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
let favoritedCount: Int?
/// A short HTML-formatted description of the media.
let synopsis: String
/// A list of genres for this title (only available in details requests).
let genres: [String]?
/// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
let tags: [String]
}
This didn't work:
Extensions may not contain stored properties.
And it had the one downside that I really didn't like: I was already duplicating code, by copying the properties of the protocol into the extension.
So in the end, I could never get my properties to be "spread" to the objects conforming to the protocol, so I ended up adding the properties to the Anime struct.
struct Anime : MediaType {
/// MARK: - MediaType
/// The ID of the media, as assigned by MyAnimeList.
let ID: Int
/// The title of the media.
let title: String
/// Other titles by which this anime may be commonly known (only available in details requests).
let otherTitles: (synonyms: [String], english: [String], japanese: [String])?
/// The global ranking of the title (only available in anime details requests).
let rank: Int?
/// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
let popularityRank: Int?
/// URL to a representative image of the title. Usually a "cover" image.
let imageURL: String
/// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
let adaptations: Relationships
/// The user's rating of the media.
let memberScore: Float
/// Number of MyAnimeList members that that added the title to their list (only available in details requests).
let membersCount: Int?
/// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
let favoritedCount: Int?
/// A short HTML-formatted description of the media.
let synopsis: String
/// A list of genres for this title (only available in details requests).
let genres: [String]?
/// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
let tags: [String]
/// MARK: - Anime
}
And this seemed to work. But now I have my properties in both MediaType and Anime. In OOP, you avoid the duplicate code by subclassing.
So I repeat my question here: Am I misunderstanding Protocol-Oriented Programming, or is the downside of POP that you have to copy-and paste your protocol-specific logic whenever you make a struct/class/enum comply with it?

It's been a few weeks since I posted this, but I believe what Aaron Brager said is true.
While Protocol-Oriented-Programming is rather new in itself, the idea of protocols has been present in Objective-C for a long time, they are in Swift, and they have their variants in languages such as Java. Due to the nature of protocols and extensions, it looks like doing default implementations of properties is not possible, as extensions won't allow you to set non-computed properties in them.

That is because your Anime struct doesn't implement all properties from the MediaType protocol. Here's a minimized example of how you can do it:
protocol MediaType : Equatable {
var ID: Int { get }
var title: String { get }
}
struct Anime : MediaType {
// Implement the MediaType protocol
var ID : Int
var title : String
}
/// Compares two Anime_ objects. Two Anime_ objects are considered equal when they have the same ID and title.
func ==(lhs: Anime, rhs: Anime) -> Bool {
return (lhs.ID == rhs.ID) && (lhs.title == rhs.title)
}
let x = Anime(ID: 1, title: "title 1")
let y = Anime(ID: 2, title: "title 2")
let z = Anime(ID: 1, title: "title 1")
println(x == y) // false
println(x == z) // true
One curiosity though: in the protocol, why are you declaring all properties as read-only?

Related

Unreliable update with #FetchRequest predicate filtering on UUID typed attributes?

I'm new-ish to Swift, and trying to better understand Core Data with some (I thought!) simple projects.
I'm trying to use an #FetchRequest property wrapper to list child-items which belong to a given parent. This works for most cases, but not when the to-parent relationship is the only thing which would cause a child item to be displayed after a new child item is added.
The problem I'm facing seems to boil down to any filtering done with UUID values using predicates in FetchRequest decorators not being updated the same way other attributes are. The minimal example I've (with an edit) constructed shows just the behaviour when using a UUID for the predicate, and avoids any relationship complications.
My question is, is there a 'nice' way to make this behaviour (UUID in a predicate driving a FetchRequest which updates views properly) work, or something I'm missing, like registering one of the parts as an observed item, or something I'm doing triggering an update at the wrong time?
I have produced an example which illustrates the puzzle I'm having. Note: I'm using iOS 14.4 and Xcode 12.4 I appreciate these are not current, but my machine does not support an OS past Catalina, so I'm stuck here until I can afford a new one.
My Core Data model is as follows:
The Item entity has two attributes (uuid, of type UUID) and an Int16 attribute (number).
The app has two buttons: + to add a new item with a random number value and a fixed UUID of 254DC... and 5 which adds a item, with a number value of 5 (and the same, fixed, UUID).
The predicate[s] I'm using to drive this list filter Item entities which either:
match the hard-coded UUID value*
or have a number attribute of 5
The problem is that when adding (using the + button) a new entity which should show up in this view just because its uuid property matches, it does not appear. It does appear if you quit the app, and return to it (this proves that the predicate does indeed 'match' the UUID values).
After tapping 5 (now showing two rows with number 5, as expected):
After tapping + twice (unchanged view, should have two new rows showing):
If we exit the app and then reopen it, the added Items (300 and 304 in this case) are only now visible:
Different phrasing of the question: Why do these updates work unreliably on UUID, but perfectly with the number (Int16-typed) attribute?
Working as expected:
Deleting Item entities works as expected, regardless of whether they were in the list because of number == 5 or their UUID match
Notes:
I've used viewContext.perform liberally, as one other glitch with an update not displaying was resolved by using that.
I had already (based on this SO question/answer) already tried wrapping each of the elements of the rows as an ObservedObject. This was also suggested in a comment. Have updated the example code to do that, which has no effect on this behaviour. (The elements are not in the items collection produced by the FetchRequest, so do not get wrapped by this code.)
There is lots of try! all over the place. This is just to keep the example code as small as possible.
View code
import SwiftUI
import CoreData
struct ItemList: View {
#Environment(\.managedObjectContext) private var viewContext
static let filterUUID = "254DC821-F654-4D45-BC94-CEEE12A428CB"
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.number, ascending: true)],
predicate: NSPredicate(format: "number == %# OR uuid == %#", NSNumber(5), filterUUID))
var items : FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in ItemRowView(item: item) }
.onDelete(perform: deleteItems)
}
.navigationTitle(Text(items.isEmpty ? "No items" : "Item count: \(items.count)"))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: addRandom) {
Label("Add Item", systemImage: "plus")
}
Button(action: addFive) {
Label("Add Five", systemImage: "5.circle.fill")
}
}
}
}
}
private func addItem(number: Int) {
viewContext.perform {
let newItem = Item(context: viewContext)
newItem.uuid = UUID(uuidString: ItemList.filterUUID)
newItem.number = Int16(number)
try! viewContext.save()
}
}
private func addRandom() { addItem(number: Int.random(in: 10...500)) }
private func addFive() { addItem(number: 5) }
private func deleteItems(offsets: IndexSet) {
viewContext.perform {
offsets.map { items[$0] }.forEach(viewContext.delete)
try! viewContext.save()
}
}
}
struct ItemRowView : View {
#ObservedObject var item : Item
var body : some View {
HStack {
Text("\(item.number)")
.font(.largeTitle)
Text(item.uuid?.uuidString.prefix(8) ?? "None")
}
}
}
What have I tried already?
I've spent the best part of two days trawling documentation, StackOverflow, and various blogs, and while there have been several things which seemed on first look to be exactly the answer I was looking for, the simpler I got my example, the more confusing it became.
This started out as a similar issue related to parent-child fetching, but I've narrowed the actual problem I'm facing down to something smaller, which can be demonstrated with the single entity with an UUID, and no relationships.
Supporting code (App and PersistenceController)
import SwiftUI
import CoreData
#main
struct minimalApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ParentListView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "minimal")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
I believe the weird behaviour is due to your predicate, which is being evaluated in two different ways. When you first run the app, or after closing and restarting, the predicate is parsed and passed to SQLite (as a WHERE clause in a SELECT statement). Thereafter, when you add new items, the predicate is evaluated directly in memory (ie in the NSManagedObjectContext) - no need to involve SQLite.
In your predicate, you are comparing a UUID type attribute with a String value - which fails. Even if the string representation of the UUID attribute is the same as the string you compare it with, the context sees them as different and regards the new object as failing the predicate. Hence the view is not updated.
However, SQLite is much more tolerant of type mismatches. (I'm guessing CoreData's implementation of UUID attributes is to store the string representation - but that's just a guess). So when you quit and restart, the fetch is processed by SQLite which regards the new object as meeting the predicate and accordingly includes it in the results.
To get the correct behaviour, I think you need your predicate to compare the UUID attribute with the UUID which has the correct string representation:
NSPredicate(format: "number == %# OR uuid == %#", NSNumber(5), UUID(uuidString: ItemList.filterUUID))

Using GridView as a template object - How to set ListModel as an alias?

I'm currently working on a large QML project, and I've come to a point where it would be advantageous to create what is essentially a template of a page, to which I can add as little information as possible in the definition of each page.
Currently, I have a MyPage.qml file, which defines objects such as the background mousearea and the elements which each page have in common such as a back button, but then I come to my problem - where not every page is alike.
The elements in each page are aligned in a grid. Initially I had used rows and columns to organize those elements, but I've come under the impression that a GridView object would be more advantageous.
As it stands, here is my implementation:
Item {
//MyPage.qml
id: page
default property alias contentmodel: content.model
...
GridView{
id: content
anchors.fill: parent
cellWidth: parent.width/4
cellHeight: parent.height/6
focus: true
model: contentmodel
delegate: Item{
height: content_background.height/6
width: content_background.width/4
property var entry: entries.split(",")
MyComboBox{
id: del_combo
height: parent.height
width: parent.width
entries: entry
}
}
}
}
This is then instantiated from an actual page definition whose important features are:
MyPage {
...
ListModel {
id: contentmodel
ListElement { entries: "string1,string2" }
ListElement { entries: "string3,string4,string5,string6,string7" }
...
}
}
In this manner, I aim to have different pages consisting of different combo boxes, pulling their similar code from the same source, and with minimal overlap.
Also worthy of note is the nature of "entries". It is a string consisting of concatenated substrings, separated by commas, which is then split back into its component parts within MyPage.qml. This is the same as recommended from QML ListElement pass list of strings
MyPage.qml is having issues receiving this string, often only providing the first element to a MyComboBox object or none at all.
So my question to you is, where did I go wrong in this implementation? Is there something fundamentally wrong with my approach? Are there alternatives to using aliasing? Do you have any example code which is able to accomplish this?
Thank you for your time.
You have defined a property contentmodel in MyPage.qml.
When using MyPagecomponent you need to attribute your ListModel to the contentmodel property, not set your ListModel id to contentmodel.
This should work :
MyPage {
contentmodel : ListModel {
ListElement { entries: "string1,string2" }
ListElement { entries: "string3,string4,string5,string6,string7" }
}
}

Orchard CMS: Connect List content part to content item

I have been struggling with what I thought would be simple.
I have a content type called Supplier. This supplier has contact information containing two addresses, one for Correspondence Address and one for Visiting Address. The supplier has also several locations, like location north and location south. A location is also an address. So basically I have a content item Supplier with a lot of addresses and all of them with their own type.
Migration:
public int Create() {
//Creating the Location contentrecord, contentpart and contenttype
SchemaBuilder.CreateTable("LocationPartRecord", table => table
.ContentPartRecord()
.Column<int>("LocationsPartRecord_id")
);
ContentDefinitionManager.AlterPartDefinition("LocationPart", part => part
.Attachable(false)
.WithField("LocationName", f => f.OfType("TextField"))
.WithField("AddressLine1", f => f.OfType("TextField"))
.WithField("AddressLine2", f => f.OfType("TextField"))
.WithField("Zipcode", f => f.OfType("TextField"))
.WithField("City", f => f.OfType("TextField"))
.WithField("Country", f => f.OfType("TextField")));
ContentDefinitionManager.AlterTypeDefinition("Location",
cfg => cfg
.WithPart("CommonPart")
.WithPart("LocationPart")
);
//Creating the Locations 'container' contentpart
SchemaBuilder.CreateTable("LocationsPartRecord", table => table
.ContentPartRecord()
);
ContentDefinitionManager.AlterPartDefinition("LocationsPart", builder => builder.Attachable());
//Creating the supplier. Specific supplier contentfields can be added later. Doing records, so I can add
//datafields later that are not contentfields
SchemaBuilder.CreateTable("SupplierPartRecord", table => table
.ContentPartRecord());
ContentDefinitionManager.AlterPartDefinition("SupplierPart", part => part
.Attachable(false)
);
ContentDefinitionManager.AlterTypeDefinition("Supplier", builder => builder
.Creatable()
.Draftable()
.WithPart("CommonPart")
.WithPart("TitlePart")
.WithPart("BodyPart")
.WithPart("AutoroutePart", partBuilder =>
partBuilder.WithSetting("AutorouteSettings.AllowCustomPattern", "true")
.WithSetting("AutorouteSettings.PatternDefinitions", "[{Name:'Supplier', Pattern: 'aanbieders/{Content.Slug}', Description: 'aanbieders/supplier-name'}]")
.WithSetting("AutorouteSettings.DefaultPatternIndex", "0"))
.WithPart("SupplierPart")
.WithPart("LocationsPart"));
return 1;
}
Models:
*LocationPartRecord and LocationPart *
public class LocationPartRecord:ContentPartRecord {
public virtual LocationsPartRecord LocationsPartRecord { get; set; }
}
public class LocationPart:ContentPart<LocationPartRecord> {
LocationsPartRecord LocationsPartRecord {
get { return Record.LocationsPartRecord; }
set { Record.LocationsPartRecord = value; }
}
}
LocationsPartRecord and LocationsPart (container)
public class LocationsPartRecord:ContentPartRecord {
public LocationsPartRecord()
{
Locations = new List<LocationPartRecord>();
}
[CascadeAllDeleteOrphan]
public virtual IList<LocationPartRecord> Locations { get; set; }
}
public class LocationsPart:ContentPart<LocationsPartRecord> {
public LocationsPart() {
Locations = new List<LocationPart>();
}
public readonly LazyField<IList<LocationPart>> _locations = new LazyField<IList<LocationPart>>();
public IList<LocationPart> Locations {
get { return _locations.Value; }
set { _locations.Value = value; }
}
}
From here I am stuck. I would like to see when Creating a new supplier, I get a screen containing all the content item fields for supplier and a list of locations, with the ability to create, delete or update a location.
I don't need the code to be spelled out, but a direction would suffice. Which drivers, controllers and views should I create. This is only for admin console. For frontend the locations need to be displayed and not edited.
I don't think there will be any way to get the functionality you're after without custom coding. As you have suggested, the comments module could be a good example to copy. The Controllers in the comments module are only to manage all of the comments in their own admin pages, separate to the content items they belong to. The edit / display of the comments is still provided through the drivers and handlers.
Using the Comments module analogy:
CommentsPart = AddressesPart - This would be added to your Supplier content type
CommentPart = AddressPart - This would be added to your Address content type
You could strip out a lot of the extra functionality that is included for managing comments and just copy the drivers, handlers, views and models for these two parts.
I have seen some gallery modules that may allow you to build these relationships through the admin interface, however I haven't used it myself:
http://gallery.orchardproject.net/List/Modules/Orchard.Module.Downplay.Mechanics
Address shouldn't be a part, it should be a field. This way, you can have more than one, and each can be named.
Don't know if this would be helpful (and the site appears to be down - but Google has a cached version if you are patient for it to load), but there is a good blog about exactly your situation. It's Skywalkers excellent Web Shop series. I believe Part 8 contains the code related to multiple addresses (uses Address and Addresses). This seems to involve your problem, and the code may be what you need.
In case you have trouble getting to the site, there is also a CodePlex repository for the code. Additionally, Bertrand's Nwazet Commerce module might have similar code.

How to create NetSuite SuiteTalk search with multiple terms?

How does one create a SuiteTalk (NetSuite web api) search query specifying multiple search terms indicating a logical OR operator?
For example, I want to retrieve TimeBill records whose create OR last modified dates are within a particular range. Here's my code that works great for a single search term. Simply adding another search term appears to create a logical AND operation.
/// <summary>
/// Return the list of time bills whose last modified date is within
/// the indicated date range.
/// </summary>
/// <param name="from">Required from date</param>
/// <param name="to">Optional to date</param>
/// <returns>List of time bills</returns>
public IEnumerable<TimeBill> GetTimeBills(DateTime from, DateTime to)
{
_log.Debug(String.Format("Enter TimeBill(DateTime from='{0}', DateTime to='{1}')", from, to));
// Build search criteria.
TimeBillSearch search = new TimeBillSearch();
TimeBillSearchBasic searchBasic = new TimeBillSearchBasic();
SearchDateField searchDateField = new SearchDateField();
searchDateField.#operator = SearchDateFieldOperator.within;
searchDateField.operatorSpecified = true;
searchDateField.searchValue = from;
searchDateField.searchValueSpecified = true;
searchDateField.searchValue2 = to;
searchDateField.searchValue2Specified = true;
searchBasic.dateCreated = searchDateField;
search.basic = searchBasic;
return this.Get<TimeBill>(search);
}
/// <summary>
/// Perform a paged search and convert the returned record to the indicated type.
/// </summary>
private IEnumerable<T> Get<T>(SearchRecord searchRecord)
{
_log.Debug("Enter Get<T>(SearchRecord searchRecord)");
// This is returned.
List<T> list = new List<T>();
// The suitetalk service return this.
SearchResult result = null;
using (ISuiteTalkService service = SuiteTalkFactory.Get<SuiteTalkService>())
{
do
{
// .search returns the first page of data.
if (result == null)
{
result = service.search(searchRecord);
}
else // .searchMore returns the next page(s) of data.
{
result = service.searchMoreWithId(result.searchId, result.pageIndex + 1);
}
if (result.status.isSuccess)
{
foreach (Record record in result.recordList)
{
if (record is T)
{
list.Add((T)Convert.ChangeType(record, typeof(T)));
}
}
}
}
while (result.pageIndex < result.totalPages);
}
return list;
}
Per the NetSuite User Community (forum), I do not believe this is currently possible (at least it does not appear in the WSDL for SuiteTalk/Web Services): https://usergroup.netsuite.com/users/showthread.php?t=29818
However, you can dynamically create a complex Saved Search (using SuiteScript in a Suitelet) that includes AND/OR and parentheses by using the nlobjSearchFilter methods setLeftParens(), setRightParens(), and setOr() (as you surmised, a logical "AND" is the default behavior when multiple filters are present).
A dynamically created Saved Search (confusing terminology here, I know) can also be saved and loaded for later reuse. You could therefore potentially take advantage of dynamically created Saved Searches on the NetSuite server by having your Web Services code invoke a Saved Search and retrieve the results, yet still have everything be dynamic (no hardcoded search filters/values).
As mentioned on the Forum, you could also potentially execute multiple searches and stitch the results together yourself (in your SuiteTalk C#/Java etc. code).

Can't access/read SIM phonebook in Nokia

am trying to access both the Phone's phonebook and SIM phonenook on Nokia 5130c-2 XpressMusic.
The app runs without errors but it only returns the numbers from the phone's Phonebook. When I list the available phonebooks using this code
String[] all_contact_lists=PIM.getInstance().listPIMLists(PIM.CONTACT_LIST);
it gives me both the Phonebook and SIM card lists. i.e
1. Phone
2. SIM
I have tried explicitly reading from the SIM card using this code but it still returns nothing(even though I have numbers saved in the SIM card.)
Code:
ContactList clist = (ContactList) PIM.getInstance().openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY,
"SIM");
Here's my complete code::
import javax.microedition.midlet.*;
import javax.microedition.pim.*;
import com.sun.lwuit.*;
import java.util.*;
public class contacts extends MIDlet
{
private List my_list=new List();
private String[] names=null;
public void startApp()
{
Display.init(this);
Form my_form=new Form("Contacts List");
String[] all_contact_lists=PIM.getInstance().listPIMLists(PIM.CONTACT_LIST);
//Iterate through available phonebooks
for(int db=0; db<all_contact_lists.length; db++)
{
try {
ContactList clist = (ContactList) PIM.getInstance().openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY,
all_contact_lists[db]);
Enumeration contacts=clist.items();
while(contacts.hasMoreElements())
{
Contact contact=(Contact)contacts.nextElement();
try{
String phone_contact="";
names=contact.getStringArray(Contact.NAME, 0);
for(int i=0; i<names.length; i++)
{
if(names[i]!=null)
phone_contact+=" "+names[i];
}
//my_list.addItem(phone_contact);
int phone_numbers=contact.countValues(Contact.TEL);
if(phone_numbers>0)
{
String number=contact.getString(Contact.TEL,0);
my_list.addItem(phone_contact+":"+number);
}
else
{
my_list.addItem(phone_contact);
}
//clist.removeContact(contact);
}
catch (Throwable t) {
t.printStackTrace();
}
}
} catch (PIMException ex) {
ex.printStackTrace();
}
}
//my_list.addItem(all_contact_lists);
my_list.setRenderingPrototype("WWWWWWWWWWWWWWWWWWWW");
my_form.addComponent(my_list);
my_form.show();
}
public void pauseApp(){}
public void destroyApp(boolean unconditional){}
}
If you are using the phone number for a text or to call you can do that with only one line of code. Its now evident that phone software issues can affect the way the app accesses the PIM API. Also, if memory in use in the phones contact settings is set to SIM(alone), you can't access contacts in phone memory and vice versa, make sure both are in use. Try this if you still have an issue,
//make a text field in LWUIT that is declared globally
PhnNmbr = new TextField();
//set it to only accept phonenumber
PhnNmbr.setConstraint(TextField.PHONENUMBER);
//tell the user how to access phonebook
PhnNmbr.setHint("Press T9 then 'Search' to search phonebook");
//add a button or command
//that either sends an sms to
//or calls the number in the text field
//or anything else you want to do with it
When the user presses T9, the TextField is considered an LCDUI text field with parameter PHONENUMBER which allows it to search for contacts in both Sim and phone memory, thats why you'll notice a Search command(usually at the bottom center). Also make sure that the memory in use for the phone is set to both Phone and Sim.
An excerpt from PIM javadoc
PIMItems reference its data through fields. A field is a grouping of
data values that all have similar characteristics. An example of a
field is TEL, which indicates data values for that particular field
are telephone numbers. Classes implementing the PIMItem interface
defines the possible fields that for that specific class (e.g TEL is
defined in the Contact interface as a field that a contact may
support).
PIM implementations are not required to support all of the possible
fields defined in the classes implementing the PIMItem interface. This
is because no native PIM databases contain all of the fields defined
in this API. The PIMList that a PIMItem belongs to determines what
fields a PIMItem can support and store (all PIMItems in a particular
PIMList support the same set of fields). The
PIMList.getSupportedFields() method from a particular PIMItem's
PIMList is used to find out what fields are supported within this
item. Since not all possible fields are actually supported in a
particular PIMItem, all fields should be checked for support in the
item's PIMList using PIMList.isSupportedField(int) prior to being used
in any retrieval or storage method.
Each field has the following pieces of information available for it:
Zero or more data values associated with the Field
Attributes for data values for the Field
Descriptive label for the Field
Data Type of the data associated with the Field
Complete PIM javadoc can be read at this link.
Check if the device support's PIM Fields Contact.NAME and Contact.TEL on your device. If not then you need to call PIMList.getSupportedFields() to get the supported field on device and accordingly fetch the name and the telephone number for that device. If I remember correct, the alternative field for name is Contact.FORMATTED_NAME.

Resources